feat: quick wins — global search, P360 actions, context panel, route guards
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

- Wire GlobalSearch component into app shell top bar (US-10)
- P360: Book Appointment button opens AppointmentForm (US-8)
- P360: Add Note button creates leadActivity via GraphQL (US-8)
- P360: Appointment rows clickable for edit (active statuses only) (US-8)
- P360: Display lead status badge (was fetched but not rendered) (US-8)
- Context panel: "View 360" link on linked patient → /patient/:id (US-6)
- Context panel: Display campaign info from lead.utmCampaign (US-6)
- Route guards: Admin-only routes wrapped in RequireAdmin (US-1, US-3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-12 13:31:56 +05:30
parent 85364c6d69
commit c044d2d143
4 changed files with 106 additions and 22 deletions

View File

@@ -1,4 +1,5 @@
import { useState, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faSparkles, faPhone, faChevronDown, faChevronUp,
@@ -58,6 +59,7 @@ const SectionHeader = ({ icon, label, count, expanded, onToggle }: {
);
export const ContextPanel = ({ selectedLead, activities, calls, followUps, appointments, patients, callerPhone, isInCall }: ContextPanelProps) => {
const navigate = useNavigate();
const [contextExpanded, setContextExpanded] = useState(true);
const [insightExpanded, setInsightExpanded] = useState(true);
const [actionsExpanded, setActionsExpanded] = useState(true);
@@ -163,6 +165,16 @@ export const ContextPanel = ({ selectedLead, activities, calls, followUps, appoi
</div>
)}
{/* Campaign info */}
{(lead.utmCampaign || lead.campaignId) && (
<div className="flex items-center gap-1.5 px-1 py-1">
<span className="text-[11px] font-semibold uppercase tracking-wider text-tertiary">Campaign</span>
<Badge size="sm" color="brand" type="pill-color">
{lead.utmCampaign ?? lead.campaignId}
</Badge>
</div>
)}
{/* Quick Actions — upcoming appointments + follow-ups + linked patient */}
{(leadAppointments.length > 0 || leadFollowUps.length > 0 || linkedPatient) && (
<div>
@@ -223,6 +235,12 @@ export const ContextPanel = ({ selectedLead, activities, calls, followUps, appoi
{linkedPatient.patientType && (
<Badge size="sm" color="gray" type="pill-color" className="ml-auto">{linkedPatient.patientType}</Badge>
)}
<button
onClick={() => navigate(`/patient/${linkedPatient.id}`)}
className="text-[11px] font-medium text-brand-secondary hover:text-brand-secondary_hover shrink-0"
>
View 360
</button>
</div>
)}
</div>