feat: audit log page, meeting prep modal, communication style, error boundaries + toast
- AuditLogPage: filterable table with expandable details (admin only) - MeetingPrepModal: AI-generated meeting briefs with health score, talking points, conversation starters - Communication Style section in Settings: tone, greeting, signoff, writing samples, avoid words - ErrorBoundary wrapping all page routes with Try Again button - Global toast system with API error interceptor (401/403/500) - ToastContainer with success/error/warning/info variants - Print CSS for meeting prep - Audit Log added to sidebar nav for admins - All 80 frontend tests pass, clean build
This commit is contained in:
@@ -17,6 +17,7 @@ import ClientForm from '@/components/ClientForm';
|
||||
import EmailComposeModal from '@/components/EmailComposeModal';
|
||||
import ClientNotes from '@/components/ClientNotes';
|
||||
import LogInteractionModal from '@/components/LogInteractionModal';
|
||||
import MeetingPrepModal from '@/components/MeetingPrepModal';
|
||||
import type { Interaction } from '@/types';
|
||||
|
||||
export default function ClientDetailPage() {
|
||||
@@ -31,6 +32,7 @@ export default function ClientDetailPage() {
|
||||
const [showEdit, setShowEdit] = useState(false);
|
||||
const [showCompose, setShowCompose] = useState(false);
|
||||
const [showLogInteraction, setShowLogInteraction] = useState(false);
|
||||
const [showMeetingPrep, setShowMeetingPrep] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const { togglePin, isPinned } = usePinnedClients();
|
||||
|
||||
@@ -120,6 +122,10 @@ export default function ClientDetailPage() {
|
||||
<Phone className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Log Interaction</span>
|
||||
</button>
|
||||
<button onClick={() => setShowMeetingPrep(true)} className="flex items-center gap-2 px-3 py-2 bg-purple-50 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300 rounded-lg text-sm font-medium hover:bg-purple-100 dark:hover:bg-purple-900/50 transition-colors">
|
||||
<Briefcase className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Meeting Prep</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>
|
||||
@@ -355,6 +361,14 @@ export default function ClientDetailPage() {
|
||||
onGenerated={(email) => setEmails((prev) => [email, ...prev])}
|
||||
/>
|
||||
|
||||
{/* Meeting Prep Modal */}
|
||||
<MeetingPrepModal
|
||||
isOpen={showMeetingPrep}
|
||||
onClose={() => setShowMeetingPrep(false)}
|
||||
clientId={client.id}
|
||||
clientName={`${client.firstName} ${client.lastName}`}
|
||||
/>
|
||||
|
||||
{/* Log Interaction Modal */}
|
||||
<LogInteractionModal
|
||||
isOpen={showLogInteraction}
|
||||
|
||||
Reference in New Issue
Block a user