diff --git a/src/components/call-desk/context-panel.tsx b/src/components/call-desk/context-panel.tsx
index 000d68c..0d842ce 100644
--- a/src/components/call-desk/context-panel.tsx
+++ b/src/components/call-desk/context-panel.tsx
@@ -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
)}
+ {/* Campaign info */}
+ {(lead.utmCampaign || lead.campaignId) && (
+
+ Campaign
+
+ {lead.utmCampaign ?? lead.campaignId}
+
+
+ )}
+
{/* Quick Actions — upcoming appointments + follow-ups + linked patient */}
{(leadAppointments.length > 0 || leadFollowUps.length > 0 || linkedPatient) && (
@@ -223,6 +235,12 @@ export const ContextPanel = ({ selectedLead, activities, calls, followUps, appoi
{linkedPatient.patientType && (
{linkedPatient.patientType}
)}
+
)}
diff --git a/src/components/layout/app-shell.tsx b/src/components/layout/app-shell.tsx
index 0a5b117..04305d5 100644
--- a/src/components/layout/app-shell.tsx
+++ b/src/components/layout/app-shell.tsx
@@ -15,6 +15,7 @@ import { useAuth } from '@/providers/auth-provider';
import { useData } from '@/providers/data-provider';
import { useMaintShortcuts } from '@/hooks/use-maint-shortcuts';
import { useNetworkStatus } from '@/hooks/use-network-status';
+import { GlobalSearch } from '@/components/shared/global-search';
import { apiClient } from '@/lib/api-client';
import { cx } from '@/utils/cx';
@@ -119,7 +120,9 @@ export const AppShell = ({ children }: AppShellProps) => {
{/* Persistent top bar — visible on all pages */}
{(hasAgentConfig || isAdmin) && (
-
+
+
+
{isAdmin &&
}
{hasAgentConfig && (
<>
@@ -140,6 +143,7 @@ export const AppShell = ({ children }: AppShellProps) => {
>
)}
+
)}
diff --git a/src/main.tsx b/src/main.tsx
index 76680e7..2e352e6 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -10,6 +10,11 @@ const AdminSetupGuard = () => {
const { isAdmin } = useAuth();
return isAdmin ?
:
;
};
+
+const RequireAdmin = () => {
+ const { isAdmin } = useAuth();
+ return isAdmin ?
:
;
+};
import { RoleRouter } from "@/components/layout/role-router";
import { NotFound } from "@/pages/not-found";
import { AllLeadsPage } from "@/pages/all-leads";
@@ -85,22 +90,23 @@ createRoot(document.getElementById("root")!).render(
} />
} />
} />
-
} />
-
} />
-
} />
-
} />
-
} />
-
} />
-
} />
-
- {/* Settings hub + section pages */}
-
} />
-
} />
-
} />
-
} />
-
} />
-
} />
-
} />
+ {/* Admin-only routes */}
+
}>
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
+
} />
} />
diff --git a/src/pages/patient-360.tsx b/src/pages/patient-360.tsx
index 62642cf..251737c 100644
--- a/src/pages/patient-360.tsx
+++ b/src/pages/patient-360.tsx
@@ -15,8 +15,10 @@ import { Avatar } from '@/components/base/avatar/avatar';
import { Badge } from '@/components/base/badges/badges';
import { Button } from '@/components/base/buttons/button';
import { ClickToCallButton } from '@/components/call-desk/click-to-call-button';
+import { AppointmentForm } from '@/components/call-desk/appointment-form';
import { apiClient } from '@/lib/api-client';
import { formatShortDate, getInitials } from '@/lib/format';
+import { notify } from '@/lib/toast';
import { cx } from '@/utils/cx';
import type { LeadActivity, LeadActivityType, Call, CallDisposition } from '@/types/entities';
@@ -96,15 +98,16 @@ type PatientData = {
};
// Appointment row component
-const AppointmentRow = ({ appt }: { appt: any }) => {
+const AppointmentRow = ({ appt, onEdit }: { appt: any; onEdit?: (appt: any) => void }) => {
const scheduledAt = appt.scheduledAt ? formatShortDate(appt.scheduledAt) : '--';
const statusColors: Record
= {
COMPLETED: 'success', SCHEDULED: 'brand', CONFIRMED: 'brand',
CANCELLED: 'error', NO_SHOW: 'warning', RESCHEDULED: 'warning',
};
+ const canEdit = appt.status !== 'COMPLETED' && appt.status !== 'CANCELLED' && appt.status !== 'NO_SHOW';
return (
-
+
canEdit && onEdit?.(appt)}>
@@ -266,6 +269,9 @@ export const Patient360Page = () => {
const { id } = useParams<{ id: string }>();
const [activeTab, setActiveTab] = useState
('appointments');
const [noteText, setNoteText] = useState('');
+ const [noteSaving, setNoteSaving] = useState(false);
+ const [apptFormOpen, setApptFormOpen] = useState(false);
+ const [editingAppt, setEditingAppt] = useState(null);
const [patient, setPatient] = useState(null);
const [loading, setLoading] = useState(true);
const [activities, setActivities] = useState([]);
@@ -383,6 +389,11 @@ export const Patient360Page = () => {
{leadInfo.source.replace(/_/g, ' ')}
)}
+ {leadInfo?.status && (
+
+ {leadInfo.status.replace(/_/g, ' ')}
+
+ )}
@@ -423,7 +434,7 @@ export const Patient360Page = () => {
{phoneRaw && (
)}
-
+
+ {
+ setApptFormOpen(false);
+ setEditingAppt(null);
+ // Refresh patient data
+ if (id) {
+ apiClient.graphql<{ patients: { edges: Array<{ node: PatientData }> } }>(
+ PATIENT_QUERY, { id }, { silent: true },
+ ).then(data => setPatient(data.patients.edges[0]?.node ?? null)).catch(() => {});
+ }
+ }}
+ />
>
);
};