feat: real notifications, interaction logging, bulk email compose
This commit is contained in:
@@ -16,6 +16,8 @@ import Modal from '@/components/Modal';
|
||||
import ClientForm from '@/components/ClientForm';
|
||||
import EmailComposeModal from '@/components/EmailComposeModal';
|
||||
import ClientNotes from '@/components/ClientNotes';
|
||||
import LogInteractionModal from '@/components/LogInteractionModal';
|
||||
import type { Interaction } from '@/types';
|
||||
|
||||
export default function ClientDetailPage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
@@ -25,8 +27,10 @@ export default function ClientDetailPage() {
|
||||
const [emails, setEmails] = useState<Email[]>([]);
|
||||
const [activities, setActivities] = useState<ActivityItem[]>([]);
|
||||
const [activeTab, setActiveTab] = useState<'info' | 'notes' | 'activity' | 'events' | 'emails'>('info');
|
||||
const [interactions, setInteractions] = useState<Interaction[]>([]);
|
||||
const [showEdit, setShowEdit] = useState(false);
|
||||
const [showCompose, setShowCompose] = useState(false);
|
||||
const [showLogInteraction, setShowLogInteraction] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const { togglePin, isPinned } = usePinnedClients();
|
||||
|
||||
@@ -36,6 +40,7 @@ export default function ClientDetailPage() {
|
||||
api.getEvents({ clientId: id }).then(setEvents).catch(() => {});
|
||||
api.getEmails({ clientId: id }).then(setEmails).catch(() => {});
|
||||
api.getClientActivity(id).then(setActivities).catch(() => {});
|
||||
api.getClientInteractions(id).then(setInteractions).catch(() => {});
|
||||
}
|
||||
}, [id, fetchClient]);
|
||||
|
||||
@@ -111,7 +116,11 @@ export default function ClientDetailPage() {
|
||||
<CheckCircle2 className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Contacted</span>
|
||||
</button>
|
||||
<button onClick={() => setShowCompose(true)} className="flex items-center gap-2 px-3 py-2 bg-blue-50 text-blue-700 rounded-lg text-sm font-medium hover:bg-blue-100 transition-colors">
|
||||
<button onClick={() => setShowLogInteraction(true)} className="flex items-center gap-2 px-3 py-2 bg-indigo-50 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300 rounded-lg text-sm font-medium hover:bg-indigo-100 dark:hover:bg-indigo-900/50 transition-colors">
|
||||
<Phone className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Log Interaction</span>
|
||||
</button>
|
||||
<button onClick={() => setShowCompose(true)} className="flex items-center gap-2 px-3 py-2 bg-blue-50 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300 rounded-lg text-sm font-medium hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-colors">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Generate Email</span>
|
||||
</button>
|
||||
@@ -260,12 +269,13 @@ export default function ClientDetailPage() {
|
||||
<div className="relative">
|
||||
{activities.map((item, index) => {
|
||||
const iconMap: Record<string, { icon: typeof Mail; color: string; bg: string }> = {
|
||||
email_sent: { icon: Send, color: 'text-emerald-600', bg: 'bg-emerald-100' },
|
||||
email_drafted: { icon: FileText, color: 'text-amber-600', bg: 'bg-amber-100' },
|
||||
event_created: { icon: Calendar, color: 'text-blue-600', bg: 'bg-blue-100' },
|
||||
client_contacted: { icon: CheckCircle2, color: 'text-emerald-600', bg: 'bg-emerald-100' },
|
||||
client_created: { icon: UserPlus, color: 'text-purple-600', bg: 'bg-purple-100' },
|
||||
client_updated: { icon: RefreshCw, color: 'text-slate-600', bg: 'bg-slate-100' },
|
||||
email_sent: { icon: Send, color: 'text-emerald-600', bg: 'bg-emerald-100 dark:bg-emerald-900/30' },
|
||||
email_drafted: { icon: FileText, color: 'text-amber-600', bg: 'bg-amber-100 dark:bg-amber-900/30' },
|
||||
event_created: { icon: Calendar, color: 'text-blue-600', bg: 'bg-blue-100 dark:bg-blue-900/30' },
|
||||
client_contacted: { icon: CheckCircle2, color: 'text-emerald-600', bg: 'bg-emerald-100 dark:bg-emerald-900/30' },
|
||||
client_created: { icon: UserPlus, color: 'text-purple-600', bg: 'bg-purple-100 dark:bg-purple-900/30' },
|
||||
client_updated: { icon: RefreshCw, color: 'text-slate-600', bg: 'bg-slate-100 dark:bg-slate-700' },
|
||||
interaction: { icon: Phone, color: 'text-indigo-600 dark:text-indigo-400', bg: 'bg-indigo-100 dark:bg-indigo-900/30' },
|
||||
};
|
||||
const { icon: Icon, color, bg } = iconMap[item.type] || iconMap.client_updated;
|
||||
|
||||
@@ -344,6 +354,22 @@ export default function ClientDetailPage() {
|
||||
clientName={`${client.firstName} ${client.lastName}`}
|
||||
onGenerated={(email) => setEmails((prev) => [email, ...prev])}
|
||||
/>
|
||||
|
||||
{/* Log Interaction Modal */}
|
||||
<LogInteractionModal
|
||||
isOpen={showLogInteraction}
|
||||
onClose={() => setShowLogInteraction(false)}
|
||||
clientId={client.id}
|
||||
clientName={`${client.firstName} ${client.lastName}`}
|
||||
onCreated={() => {
|
||||
// Refresh interactions and activity
|
||||
if (id) {
|
||||
api.getClientInteractions(id).then(setInteractions).catch(() => {});
|
||||
api.getClientActivity(id).then(setActivities).catch(() => {});
|
||||
fetchClient(id); // refresh lastContactedAt
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user