Skip to content

Commit ac161c4

Browse files
authored
v1.2.6x - Lists and accounts module upgrades.
2 parents fc83f50 + 1c295ca commit ac161c4

File tree

13 files changed

+435
-29
lines changed

13 files changed

+435
-29
lines changed

app/(auth)/sign-in/components/LoginComponent.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ export function LoginComponent() {
7777
description: "You have been logged out.",
7878
});
7979
hasToastedLogout.current = true;
80+
81+
// Clean up the URL to prevent any re-triggers if the component remounts natively
82+
const newUrl = new URL(window.location.href);
83+
newUrl.searchParams.delete("loggedOut");
84+
window.history.replaceState({}, document.title, newUrl.toString());
8085
}
8186
}, [searchParams, toast]);
8287

@@ -448,14 +453,29 @@ export function LoginComponent() {
448453
<div className="space-y-4">
449454
{mfaMethod === "TOTP" ? (
450455
<>
451-
<div className="w-full">
452-
<Input
453-
placeholder="000 000"
454-
className="block text-center text-3xl font-mono h-16 bg-white/5 border-white/10 tracking-widest"
456+
<div className="relative w-full max-w-[280px] mx-auto h-16">
457+
{/* Visual 6-Box Layout */}
458+
<div className="absolute inset-0 flex justify-between pointer-events-none">
459+
{[...Array(6)].map((_, i) => (
460+
<div
461+
key={i}
462+
className={`w-10 h-16 flex items-center justify-center text-3xl font-mono rounded-md border ${
463+
mfaCode.length === i
464+
? 'border-primary ring-1 ring-primary focus:bg-white/10'
465+
: 'border-white/20 bg-white/5'
466+
}`}
467+
>
468+
{mfaCode[i] || ""}
469+
</div>
470+
))}
471+
</div>
472+
473+
{/* Invisible Native Input for seamless autofill, touch, and typing */}
474+
<input
475+
className="absolute inset-0 w-full h-full opacity-0 cursor-text z-10"
455476
maxLength={6}
456477
inputMode="numeric"
457478
autoComplete="one-time-code"
458-
aria-label="Verification code"
459479
value={mfaCode}
460480
onChange={(e) => setMfaCode(e.target.value.replace(/\D/g, ''))}
461481
autoFocus

app/(routes)/crm/accounts/components/ImportLeadsDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

33
import { useState, useRef } from "react";
4-
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
4+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogTrigger } from "@/components/ui/dialog";
55
import { Button } from "@/components/ui/button";
66
import { Switch } from "@/components/ui/switch";
77
import { motion, AnimatePresence } from "framer-motion";
@@ -226,7 +226,7 @@ export default function ImportLeadsDialog({ pools, onCommitted }: Props) {
226226
<DialogTitle className="text-3xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-white to-zinc-400">
227227
Import Intelligence
228228
</DialogTitle>
229-
<p className="text-zinc-500 text-sm mt-1">Transform your lists into actionable pipeline with AI-powered normalization.</p>
229+
<DialogDescription className="text-zinc-500 text-sm mt-1">Transform your lists into actionable pipeline with AI-powered normalization.</DialogDescription>
230230
</div>
231231
</div>
232232
</DialogHeader>
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
2+
import { Badge } from "@/components/ui/badge";
3+
import { Mail, Building2, Phone } from "lucide-react";
4+
import moment from "moment";
5+
6+
interface Props {
7+
lead: any | null;
8+
onClose: () => void;
9+
getStatusStyles: (status: string) => string;
10+
}
11+
12+
export function ListLeadDetailsModal({ lead, onClose, getStatusStyles }: Props) {
13+
if (!lead) return null;
14+
15+
const contacts = lead._allContacts || [{
16+
id: lead.id,
17+
firstName: lead.firstName,
18+
lastName: lead.lastName,
19+
email: lead.email,
20+
phone: lead.phone,
21+
jobTitle: lead.jobTitle,
22+
contactId: lead.contactId,
23+
}];
24+
25+
const uniqueEmails = Array.from(new Set(contacts.map((c: any) => c.email).filter(Boolean))) as string[];
26+
const uniquePhones = Array.from(new Set(contacts.map((c: any) => c.phone).filter(Boolean))) as string[];
27+
28+
return (
29+
<Dialog open={!!lead} onOpenChange={(v) => !v && onClose()}>
30+
<DialogContent className="max-w-2xl p-0 overflow-hidden bg-zinc-950 border-zinc-800 shadow-2xl">
31+
<div className="p-6 sm:p-8">
32+
<DialogHeader className="mb-6">
33+
<div className="flex items-center gap-4">
34+
<div className="p-3.5 rounded-2xl bg-indigo-500/10 border border-indigo-500/20 text-indigo-400">
35+
<Building2 className="w-6 h-6" />
36+
</div>
37+
<div>
38+
<DialogTitle className="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-white to-zinc-400 tracking-tight">
39+
{lead.company || lead.lastName || "Unknown Account"}
40+
</DialogTitle>
41+
<DialogDescription className="text-zinc-500 text-sm mt-1">
42+
List Details & Discovered Intelligence
43+
</DialogDescription>
44+
</div>
45+
</div>
46+
</DialogHeader>
47+
48+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
49+
<div className="space-y-6">
50+
<div>
51+
<h4 className="text-[10px] font-bold text-zinc-500 uppercase tracking-widest mb-3 italic">Account Routing</h4>
52+
<div className="p-5 rounded-2xl border border-zinc-800/50 bg-zinc-900/50 space-y-4 hover:border-indigo-500/30 transition-colors">
53+
<div className="flex items-start gap-3">
54+
<Mail className="w-4 h-4 text-zinc-500 shrink-0 mt-0.5" />
55+
<div className="flex flex-col gap-2 w-full min-w-0">
56+
<div>
57+
<span className="text-xs font-medium text-zinc-400 block mb-1">Primary Email</span>
58+
<div className="text-sm font-semibold truncate text-zinc-200 bg-zinc-950 border border-zinc-800 px-3 py-1.5 rounded-lg inline-block">
59+
{uniqueEmails[0] || "No primary email"}
60+
</div>
61+
</div>
62+
63+
{uniqueEmails.length > 1 && (
64+
<div className="pt-2 border-t border-zinc-800/50">
65+
<span className="text-xs font-medium text-zinc-400 block mb-2">Additional Intelligence</span>
66+
<div className="flex flex-col gap-1.5">
67+
{uniqueEmails.slice(1).map((e, idx) => (
68+
<Badge variant="secondary" key={idx} className="font-normal text-xs bg-indigo-500/10 text-indigo-300 hover:bg-indigo-500/20 border-indigo-500/20 justify-start px-2.5 py-1">
69+
{e}
70+
</Badge>
71+
))}
72+
</div>
73+
</div>
74+
)}
75+
</div>
76+
</div>
77+
</div>
78+
</div>
79+
80+
<div>
81+
<h4 className="text-[10px] font-bold text-zinc-500 uppercase tracking-widest mb-3 italic">Lifecycle Status</h4>
82+
<div className="p-5 rounded-2xl border border-zinc-800/50 bg-zinc-900/50 space-y-3 hover:border-zinc-700 transition-colors">
83+
{lead.status && (
84+
<div className="flex justify-between items-center text-sm">
85+
<span className="text-zinc-400">Pipeline Status</span>
86+
<Badge variant="outline" className={`font-mono text-[10px] uppercase tracking-wider border ${getStatusStyles(lead.status)}`}>
87+
{lead.status === 'CONVERTED' ? 'In CRM' : (lead.status || "New")}
88+
</Badge>
89+
</div>
90+
)}
91+
{uniquePhones.length > 0 && (
92+
<div className="flex justify-between items-start text-sm pt-2 border-t border-zinc-800/50">
93+
<div className="flex items-center gap-2 text-zinc-400">
94+
<Phone className="w-3 h-3" />
95+
<span>Phones</span>
96+
</div>
97+
<div className="flex flex-col text-right">
98+
{uniquePhones.map((p, i) => (
99+
<span key={i} className="font-medium text-zinc-300">{p}</span>
100+
))}
101+
</div>
102+
</div>
103+
)}
104+
</div>
105+
</div>
106+
</div>
107+
108+
<div className="space-y-6">
109+
<div>
110+
<h4 className="text-[10px] font-bold text-zinc-500 uppercase tracking-widest mb-3 italic">Decision Makers</h4>
111+
<div className="p-5 rounded-2xl border border-zinc-800/50 bg-zinc-900/50 space-y-3 hover:border-zinc-700 transition-colors">
112+
{contacts && contacts.length > 0 ? (
113+
<div className="space-y-3">
114+
{contacts.map((c: any, i: number) => (
115+
<div key={i} className="flex items-start gap-3 pt-3 border-t border-zinc-800/50 first:pt-0 first:border-0">
116+
<div className="w-8 h-8 rounded-full bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-emerald-400 font-bold text-xs shrink-0 mt-0.5">
117+
{c.firstName?.[0] || ""}{c.lastName?.[0] || ""}
118+
</div>
119+
<div className="flex flex-col min-w-0">
120+
<span className="text-sm font-medium text-zinc-200">
121+
{c.firstName} {c.lastName}
122+
</span>
123+
{c.jobTitle && (
124+
<span className="text-xs text-zinc-500 truncate">{c.jobTitle}</span>
125+
)}
126+
<div className="flex items-center gap-2 mt-1">
127+
{c.email && <span className="text-[10px] text-zinc-400 truncate border border-zinc-800/50 rounded px-1">{c.email}</span>}
128+
{c.contactId && <Badge variant="outline" className="text-[8px] h-4 py-0 px-1 border-indigo-500/20 text-indigo-400 bg-indigo-500/10">In CRM</Badge>}
129+
</div>
130+
</div>
131+
</div>
132+
))}
133+
</div>
134+
) : (
135+
<div className="text-center py-4">
136+
<p className="text-xs text-zinc-500 italic">No contacts registered.</p>
137+
</div>
138+
)}
139+
</div>
140+
</div>
141+
142+
<div>
143+
<h4 className="text-[10px] font-bold text-zinc-500 uppercase tracking-widest mb-3 italic">System Footprint</h4>
144+
<div className="p-5 rounded-2xl border border-zinc-800/50 bg-zinc-900/50 space-y-3 hover:border-zinc-700 transition-colors">
145+
<div className="flex justify-between items-center text-sm">
146+
<span className="text-zinc-400">Imported On</span>
147+
<span className="text-zinc-300 font-medium">
148+
{lead.createdAt ? moment(lead.createdAt).format("MMM Do YYYY, h:mm a") : "Unknown"}
149+
</span>
150+
</div>
151+
{lead.accountId && (
152+
<div className="flex justify-between items-center text-sm pt-2 border-t border-zinc-800/50">
153+
<span className="text-zinc-400">Account ID</span>
154+
<span className="font-mono text-[10px] bg-zinc-950 px-2 py-1 rounded border border-zinc-800 text-zinc-500">
155+
{lead.accountId}
156+
</span>
157+
</div>
158+
)}
159+
<div className="flex justify-between items-center text-sm pt-2 border-t border-zinc-800/50">
160+
<span className="text-zinc-400">Lead Record ID</span>
161+
<span className="font-mono text-[10px] bg-zinc-950 px-2 py-1 rounded border border-zinc-800 text-zinc-500">
162+
{lead.id.slice(0, 12)}...
163+
</span>
164+
</div>
165+
</div>
166+
</div>
167+
</div>
168+
</div>
169+
</div>
170+
</DialogContent>
171+
</Dialog>
172+
);
173+
}

app/(routes)/crm/accounts/lists/[listId]/page.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
AlertDialogHeader,
3232
AlertDialogTitle,
3333
} from "@/components/ui/alert-dialog";
34+
import { ListLeadDetailsModal } from "./components/list-lead-details-modal";
3435

3536
type Lead = {
3637
id: string;
@@ -94,6 +95,7 @@ export default function AccountListDetailsPage() {
9495
mutateLeads();
9596
};
9697

98+
const [selectedLead, setSelectedLead] = useState<Lead | null>(null);
9799
const [deleting, setDeleting] = useState<string | null>(null);
98100
const [leadToDelete, setLeadToDelete] = useState<Lead | null>(null);
99101

@@ -355,11 +357,15 @@ export default function AccountListDetailsPage() {
355357
const uniquePhones = Array.from(new Set(contacts.map(c => c.phone).filter(Boolean)));
356358

357359
return (
358-
<TableRow key={lead.id} className="group hover:bg-muted/30 transition-colors align-top">
360+
<TableRow
361+
key={lead.id}
362+
className="group hover:bg-muted/30 transition-colors align-top cursor-pointer"
363+
onClick={() => setSelectedLead(lead)}
364+
>
359365
<TableCell className="font-semibold text-primary">
360366
{lead.accountId ? (
361367
<button
362-
onClick={() => router.push(`/crm/accounts/${lead.accountId}`)}
368+
onClick={(e) => { e.stopPropagation(); router.push(`/crm/accounts/${lead.accountId}`); }}
363369
className="hover:underline text-left transition-colors"
364370
>
365371
{lead.company || lead.lastName}
@@ -368,12 +374,15 @@ export default function AccountListDetailsPage() {
368374
<span>{lead.company || lead.lastName}</span>
369375
)}
370376
</TableCell>
371-
<TableCell className="font-medium text-muted-foreground group-hover:text-foreground transition-colors">
377+
<TableCell className="font-medium text-muted-foreground group-hover:text-foreground transition-colors max-w-[200px]">
372378
{uniqueEmails.length > 0 ? (
373-
<div className="flex flex-col gap-0.5">
374-
{uniqueEmails.map((email, i) => (
375-
<span key={i} className="text-sm">{email}</span>
376-
))}
379+
<div className="flex items-center gap-2 truncate">
380+
<span className="truncate text-sm">{uniqueEmails[0]}</span>
381+
{uniqueEmails.length > 1 && (
382+
<span className="text-[10px] font-bold px-1.5 py-0.5 rounded-md bg-zinc-800 text-zinc-300 border border-zinc-700 shrink-0">
383+
+{uniqueEmails.length - 1}
384+
</span>
385+
)}
377386
</div>
378387
) : "—"}
379388
</TableCell>
@@ -541,6 +550,12 @@ export default function AccountListDetailsPage() {
541550
</AlertDialogFooter>
542551
</AlertDialogContent>
543552
</AlertDialog>
553+
554+
<ListLeadDetailsModal
555+
lead={selectedLead}
556+
onClose={() => setSelectedLead(null)}
557+
getStatusStyles={getStatusStyles}
558+
/>
544559
</div>
545560
);
546561
}

app/(routes)/crm/accounts/table-components/account-card.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import { Account } from "../table-data/schema";
1414

1515
interface AccountCardProps {
1616
row: Row<Account>;
17+
onClick?: (account: Account) => void;
1718
}
1819

19-
export function AccountCard({ row }: AccountCardProps) {
20+
export function AccountCard({ row, onClick }: AccountCardProps) {
2021
const account = row.original;
2122

2223
return (
23-
<Card className="hover:shadow-md transition-shadow">
24+
<Card className="hover:shadow-md transition-shadow cursor-pointer" onClick={() => onClick?.(account)}>
2425
<CardHeader className="p-4 pb-2 flex flex-row items-start justify-between space-y-0">
2526
<div className="space-y-1">
2627
<div className="font-semibold flex items-center gap-2">

0 commit comments

Comments
 (0)