Frontend Migration Guide - LHR Implementation
Overview
This guide outlines the frontend changes required to migrate from the current careplan system to the new FHIR-compliant Longitudinal Health Records architecture. The backend has been updated to rename careplans table to prescriptions and change all encounter_id references to appointment_id.
Backend Changes Summary
Database Changes Made:
careplanstable →prescriptionstable- All
encounter_idcolumns →appointment_idcolumns - Polymorphic type references:
'Careplan'→'Prescription' - Event tracking:
'careplan.create'→'prescription.create' - Model associations:
.careplans→.prescriptions
New FHIR API Endpoints Available:
All endpoints use RESTful routes nested under appointments:
/appointments/:appointment_id/clinical_observations/vitals- Vitals management/appointments/:appointment_id/clinical_impressions- Clinical assessments/appointments/:appointment_id/medication_requests- Medications/appointments/:appointment_id/service_requests- Investigations/appointments/:appointment_id/care_plans/advice- Clinical advice/appointments/:appointment_id/care_plans/follow_up- Next visit planning
Phase 1: API Client Updates
Step 1: Update Type Definitions
Update Medical Types
// frontend/packages/api-client/src/types/medical.ts
// Updated Prescription interface (was Careplan)
export interface Prescription {
id: number;
patient_id: number;
doctor_id: number;
appointment_id: number; // CHANGED: was encounter_id
clinic_id: number;
prescription_date: string;
diagnosis: string[];
observations: string;
notes: string;
followup_date?: string;
is_final: boolean;
created_at: string;
updated_at: string;
// Related data
prescription_medicines: PrescriptionMedicine[];
prescription_test_lists: PrescriptionTestList[];
medication_requests: MedicationRequest[];
service_requests: ServiceRequest[];
}
// New FHIR resource interfaces
export interface MedicationRequest {
id: number;
clinic_id: number;
patient_id: number;
doctor_id: number;
appointment_id: number; // CHANGED: was encounter_id
prescription_id: number;
status: 'active' | 'on-hold' | 'cancelled' | 'completed';
intent: 'proposal' | 'plan' | 'order';
medication_name: string;
medication_type?: string;
strength?: string;
form?: string;
dosage_instruction?: string;
frequency?: string;
quantity_value?: number;
quantity_unit?: string;
duration_value?: number;
duration_unit?: string;
instructions?: string;
as_needed: boolean;
document_generated: boolean;
document_url?: string;
created_at: string;
updated_at: string;
// Relations
patient?: Patient;
doctor?: Doctor;
appointment?: Appointment; // CHANGED: was encounter
}
export interface ServiceRequest {
id: number;
clinic_id: number;
patient_id: number;
doctor_id: number;
appointment_id: number; // CHANGED: was encounter_id
prescription_id: number;
status: 'active' | 'on-hold' | 'cancelled' | 'completed';
intent: 'proposal' | 'plan' | 'order';
service_type: 'lab_test' | 'imaging' | 'procedure';
service_name: string;
service_code?: string;
instructions?: string;
document_generated: boolean;
document_url?: string;
created_at: string;
updated_at: string;
}
export interface PatientProblem {
id: number;
clinic_id: number;
patient_id: number;
doctor_id: number;
problem_code?: string;
problem_name: string;
problem_category?: string;
severity?: string;
onset_date?: string;
resolved_date?: string;
status: 'active' | 'resolved' | 'chronic';
first_appointment_id?: number; // CHANGED: was first_encounter_id
last_appointment_id?: number; // CHANGED: was last_encounter_id
clinical_notes?: string;
created_at: string;
updated_at: string;
}
export interface CarePlan {
id: number;
clinic_id: number;
patient_id: number;
doctor_id: number;
status: 'active' | 'completed' | 'cancelled';
intent: 'proposal' | 'plan' | 'order';
title?: string;
description?: string;
period_start?: string;
period_end?: string;
goals: Record<string, any>;
activities: Record<string, any>;
care_team: any[];
addresses_problems: number[];
document_generated: boolean;
document_url?: string;
created_at: string;
updated_at: string;
}Update Longitudinal Data Interface
// frontend/packages/api-client/src/types/longitudinal.ts
export interface PatientLongitudinalData {
patient: Patient;
current_medications: PatientMedicine[];
current_problems: PatientProblem[];
active_care_plans: CarePlan[];
encounter_history: EncounterSummary[];
medication_history: MedicationRequest[];
service_history: ServiceRequest[];
health_trends: {
vitals_timeline: VitalsTrend[];
weight_timeline: WeightTrend[];
medication_adherence: MedicationAdherence[];
problem_timeline: ProblemTrend[];
};
}
export interface EncounterSummary {
id: number;
appointment_date: string;
appointment_type: string;
doctor: Doctor;
diagnosis: string[];
observations: string;
prescription?: Prescription;
vitals?: Record<string, any>;
}Step 2: Update API Client Functions
Create New FHIR-Compliant API Clients
// frontend/packages/api-client/src/api/vitals.ts
export const vitalsApi = {
getVitals: (appointmentId: number) =>
apiClient.get<VitalSigns>(`/appointments/${appointmentId}/clinical_observations/vitals`),
updateVitals: (appointmentId: number, data: VitalSignsData) =>
apiClient.put<VitalSigns>(`/appointments/${appointmentId}/clinical_observations/vitals`, data),
};
// frontend/packages/api-client/src/api/clinical-impressions.ts
export const clinicalImpressionsApi = {
getClinicalImpressions: (appointmentId: number) =>
apiClient.get<ClinicalImpression[]>(`/appointments/${appointmentId}/clinical_impressions`),
updateClinicalImpressions: (appointmentId: number, data: ClinicalImpressionData) =>
apiClient.put<ClinicalImpression>(`/appointments/${appointmentId}/clinical_impressions`, data),
};
// frontend/packages/api-client/src/api/medication-requests.ts
export const medicationRequestsApi = {
getMedications: (appointmentId: number) =>
apiClient.get<MedicationRequest[]>(`/appointments/${appointmentId}/medication_requests`),
addMedication: (appointmentId: number, data: MedicationRequestData) =>
apiClient.post<MedicationRequest>(`/appointments/${appointmentId}/medication_requests`, data),
searchMedications: (search: string) =>
apiClient.get<Medication[]>(`/medications?search=${encodeURIComponent(search)}`),
};
// frontend/packages/api-client/src/api/service-requests.ts
export const serviceRequestsApi = {
getServiceRequests: (appointmentId: number) =>
apiClient.get<ServiceRequest[]>(`/appointments/${appointmentId}/service_requests`),
orderInvestigation: (appointmentId: number, data: ServiceRequestData) =>
apiClient.post<ServiceRequest>(`/appointments/${appointmentId}/service_requests`, data),
searchInvestigations: (params: { category?: string; search?: string }) =>
apiClient.get<Investigation[]>('/investigations', { params }),
};
// frontend/packages/api-client/src/api/care-plans.ts
export const carePlansApi = {
getAdvice: (appointmentId: number) =>
apiClient.get<AdviceData>(`/appointments/${appointmentId}/care_plans/advice`),
updateAdvice: (appointmentId: number, data: AdviceData) =>
apiClient.put<AdviceData>(`/appointments/${appointmentId}/care_plans/advice`, data),
getFollowUpPlan: (appointmentId: number) =>
apiClient.get<FollowUpPlan>(`/appointments/${appointmentId}/care_plans/follow_up`),
scheduleFollowUp: (appointmentId: number, data: FollowUpData) =>
apiClient.post<FollowUpPlan>(`/appointments/${appointmentId}/care_plans/follow_up`, data),
};
// frontend/packages/api-client/src/api/longitudinal-health-records.ts
export const lhrApi = {
getPatientLHR: (patientId: number) =>
apiClient.get<PatientLongitudinalData>(`/patients/${patientId}/longitudinal_health_record`),
getPatientProblems: (patientId: number) =>
apiClient.get<PatientProblem[]>(`/patients/${patientId}/problems`),
getMedicationTimeline: (patientId: number) =>
apiClient.get<MedicationRequest[]>(`/patients/${patientId}/medications_timeline`),
};Step 3: Update API Client Index
// frontend/packages/api-client/src/api/index.ts
// Remove old careplan exports
// export { careplansApi } from './careplans';
// Add new FHIR-compliant exports
export { vitalsApi } from './vitals';
export { clinicalImpressionsApi } from './clinical-impressions';
export { medicationRequestsApi } from './medication-requests';
export { serviceRequestsApi } from './service-requests';
export { carePlansApi } from './care-plans';
export { lhrApi } from './longitudinal-health-records';Phase 2: CMS App Updates
Step 1: Create New FHIR Tab Components
Vitals Tab Component
// frontend/apps/cms/src/components/lhr-tabs/VitalsTab.tsx
import { vitalsApi } from '@kulcare/api-client';
import type { VitalSigns } from '@kulcare/api-client/types';
export function VitalsTab({ appointmentId }: { appointmentId: number }) {
const [vitals, setVitals] = useState<VitalSigns | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadVitals();
}, [appointmentId]);
const loadVitals = async () => {
try {
setLoading(true);
const result = await vitalsApi.getVitals(appointmentId);
setVitals(result.data);
} catch (error) {
console.error('Failed to load vitals:', error);
} finally {
setLoading(false);
}
};
const updateVitals = async (data: VitalSignsData) => {
try {
await vitalsApi.updateVitals(appointmentId, data);
loadVitals(); // Reload data
toast.success('Vitals updated successfully');
} catch (error) {
toast.error('Failed to update vitals');
}
};
if (loading) return <LoadingSpinner />;
return (
<div className="vitals-tab">
<VitalSignsForm
initialData={vitals}
onSubmit={updateVitals}
/>
</div>
);
}Medications Tab Component
// frontend/apps/cms/src/components/lhr-tabs/MedicationsTab.tsx
import { medicationRequestsApi } from '@kulcare/api-client';
import type { MedicationRequest } from '@kulcare/api-client/types';
export function MedicationsTab({ appointmentId }: { appointmentId: number }) {
const [medications, setMedications] = useState<MedicationRequest[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadMedications();
}, [appointmentId]);
const loadMedications = async () => {
try {
setLoading(true);
const result = await medicationRequestsApi.getMedications(appointmentId);
setMedications(result.data);
} catch (error) {
console.error('Failed to load medications:', error);
} finally {
setLoading(false);
}
};
const addMedication = async (data: MedicationRequestData) => {
try {
await medicationRequestsApi.addMedication(appointmentId, data);
loadMedications(); // Reload data
toast.success('Medication added successfully');
} catch (error) {
toast.error('Failed to add medication');
}
};
const searchMedications = async (search: string) => {
try {
const result = await medicationRequestsApi.searchMedications(search);
return result.data;
} catch (error) {
console.error('Failed to search medications:', error);
return [];
}
};
return (
<div className="medications-tab">
<MedicationList medications={medications} />
<AddMedicationForm
onSubmit={addMedication}
onSearch={searchMedications}
/>
</div>
);
}Step 2: Create New FHIR Resource Components
MedicationRequestList Component
// frontend/apps/cms/src/components/medication-requests/MedicationRequestList.tsx
import { medicationRequestsApi } from '@kulcare/api-client';
import type { MedicationRequest } from '@kulcare/api-client/types';
export function MedicationRequestList({ patientId }: { patientId: number }) {
const [requests, setRequests] = useState<MedicationRequest[]>([]);
useEffect(() => {
loadMedicationRequests();
}, [patientId]);
const loadMedicationRequests = async () => {
try {
const result = await medicationRequestsApi.list({ patient_id: patientId });
setRequests(result.data);
} catch (error) {
console.error('Failed to load medication requests:', error);
}
};
const updateStatus = async (id: number, status: string) => {
try {
await medicationRequestsApi.updateStatus(id, status);
loadMedicationRequests(); // Reload data
toast.success('Status updated');
} catch (error) {
toast.error('Failed to update status');
}
};
return (
<div className="medication-requests-list">
{requests.map(request => (
<MedicationRequestCard
key={request.id}
request={request}
onStatusUpdate={updateStatus}
/>
))}
</div>
);
}Step 3: Update LHR Components
Update PatientLHR Component
// frontend/apps/cms/src/components/patient/PatientLHR.tsx
import { lhrApi } from '@kulcare/api-client';
import type { PatientLongitudinalData } from '@kulcare/api-client/types';
export function PatientLHR({ patientId }: { patientId: number }) {
const [lhrData, setLhrData] = useState<PatientLongitudinalData | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadLHRData();
}, [patientId]);
const loadLHRData = async () => {
try {
setLoading(true);
const result = await lhrApi.getPatientLHR(patientId);
setLhrData(result.data);
} catch (error) {
console.error('Failed to load LHR data:', error);
toast.error('Failed to load patient health record');
} finally {
setLoading(false);
}
};
if (loading) return <LoadingSpinner />;
if (!lhrData) return <div>No health record data available</div>;
return (
<div className="patient-lhr">
<LHRSummary data={lhrData} />
<div className="lhr-sections">
<CurrentProblemsSection problems={lhrData.current_problems} />
<CurrentMedicationsSection medications={lhrData.current_medications} />
<EncounterHistorySection encounters={lhrData.encounter_history} />
<HealthTrendsSection trends={lhrData.health_trends} />
</div>
</div>
);
}Phase 3: Routing Updates
Step 1: Update Route Definitions
New FHIR-Compliant Routes
// frontend/apps/cms/src/routes/index.tsx
const routes = [
// Legacy route redirects (temporary backward compatibility)
{
path: '/patients/:patientId/careplans',
element: <Navigate to="/cms/prescription/patient/:patientId" replace />,
},
// New FHIR LHR route structure
{
path: '/cms/prescription/patient/:patientId/:appointmentId',
element: <LHRPrescriptionPage />,
children: [
{ path: 'vitals', element: <VitalsTab /> },
{ path: 'observations', element: <ObservationsTab /> },
{ path: 'medications', element: <MedicationsTab /> },
{ path: 'investigations', element: <InvestigationsTab /> },
{ path: 'advice', element: <AdviceTab /> },
{ path: 'next-visit', element: <NextVisitTab /> },
],
},
// Patient overview with LHR integration
{
path: '/patients/:patientId/longitudinal-health-record',
element: <PatientLHRPage />,
},
];LHR Prescription Page Component
// frontend/apps/cms/src/pages/LHRPrescriptionPage.tsx
import { Outlet, useParams } from 'react-router-dom';
export function LHRPrescriptionPage() {
const { patientId, appointmentId } = useParams();
return (
<div className="lhr-prescription-page">
<LHRHeader patientId={patientId} appointmentId={appointmentId} />
<div className="lhr-tabs">
<LHRTabNavigation />
<div className="tab-content">
<Outlet context={{ patientId, appointmentId }} />
</div>
</div>
</div>
);
}Step 2: Update Navigation
Patient Menu Updates
// frontend/apps/cms/src/components/patient/PatientMenu.tsx
const menuItems = [
{
label: 'Overview',
path: `/patients/${patientId}`,
icon: <UserIcon />,
},
{
label: 'Appointments',
path: `/patients/${patientId}/appointments`,
icon: <CalendarIcon />,
},
// Update existing menu item to new LHR structure
{
label: 'Create Prescription',
path: `/cms/prescription/patient/${patientId}/${appointmentId}`,
icon: <PrescriptionIcon />,
},
// Add new menu items
{
label: 'Health Timeline',
path: `/patients/${patientId}/longitudinal-health-record`,
icon: <TimelineIcon />,
},
];LHR Tab Navigation
// frontend/apps/cms/src/components/lhr/LHRTabNavigation.tsx
const tabItems = [
{ key: 'vitals', label: 'Vitals', icon: <HeartIcon /> },
{ key: 'observations', label: 'Observations', icon: <ClipboardIcon /> },
{ key: 'medications', label: 'Medications', icon: <PillIcon /> },
{ key: 'investigations', label: 'Investigations', icon: <TestTubeIcon /> },
{ key: 'advice', label: 'Advice', icon: <MessageCircleIcon /> },
{ key: 'next-visit', label: 'Next Visit', icon: <CalendarPlusIcon /> },
];
export function LHRTabNavigation() {
const { patientId, appointmentId } = useParams();
const location = useLocation();
return (
<nav className="lhr-tab-navigation">
{tabItems.map((tab) => (
<Link
key={tab.key}
to={`/cms/prescription/patient/${patientId}/${appointmentId}/${tab.key}`}
className={cn(
'tab-link',
location.pathname.endsWith(tab.key) && 'active'
)}
>
{tab.icon}
{tab.label}
</Link>
))}
</nav>
);
}Phase 4: State Management Updates
Step 1: Update Store Slices
Rename Careplan Store
// frontend/apps/cms/src/store/combined-prescriptions.ts (renamed from careplans.ts)
import { create } from 'zustand';
import { combinedPrescriptionsApi } from '@kulcare/api-client';
import type { Prescription } from '@kulcare/api-client/types';
interface PrescriptionStore {
prescriptions: Prescription[];
loading: boolean;
error: string | null;
// Actions
loadPrescriptions: (patientId: number) => Promise<void>;
createPrescription: (data: any) => Promise<void>;
updatePrescription: (id: number, data: any) => Promise<void>;
deletePrescription: (id: number) => Promise<void>;
}
export const usePrescriptionStore = create<PrescriptionStore>((set, get) => ({
prescriptions: [],
loading: false,
error: null,
loadPrescriptions: async (patientId: number) => {
set({ loading: true, error: null });
try {
const result = await combinedPrescriptionsApi.list({ patient_id: patientId });
set({ prescriptions: result.data, loading: false });
} catch (error) {
set({ error: 'Failed to load prescriptions', loading: false });
}
},
createPrescription: async (data: any) => {
try {
const result = await combinedPrescriptionsApi.create(data);
set(state => ({
prescriptions: [result.data, ...state.prescriptions]
}));
} catch (error) {
set({ error: 'Failed to create prescription' });
}
},
}));Create New FHIR Store
// frontend/apps/cms/src/store/medication-requests.ts
import { create } from 'zustand';
import { medicationRequestsApi } from '@kulcare/api-client';
import type { MedicationRequest } from '@kulcare/api-client/types';
interface MedicationRequestStore {
requests: MedicationRequest[];
loading: boolean;
error: string | null;
loadRequests: (patientId: number) => Promise<void>;
updateStatus: (id: number, status: string) => Promise<void>;
createRequest: (data: any) => Promise<void>;
}
export const useMedicationRequestStore = create<MedicationRequestStore>((set) => ({
requests: [],
loading: false,
error: null,
loadRequests: async (patientId: number) => {
set({ loading: true, error: null });
try {
const result = await medicationRequestsApi.list({ patient_id: patientId });
set({ requests: result.data, loading: false });
} catch (error) {
set({ error: 'Failed to load medication requests', loading: false });
}
},
updateStatus: async (id: number, status: string) => {
try {
await medicationRequestsApi.updateStatus(id, status);
set(state => ({
requests: state.requests.map(req =>
req.id === id ? { ...req, status } : req
)
}));
} catch (error) {
set({ error: 'Failed to update status' });
}
},
}));Phase 5: Component Migration
Step 1: Update Existing Components
Files to Update:
- All components importing from
@kulcare/api-client/careplans - Components using
Careplantype - change toPrescription - Route links pointing to
/careplans/- change to/combined-prescriptions/
Search and Replace Operations:
# In frontend/apps/cms/src/
find . -name "*.tsx" -o -name "*.ts" | xargs sed -i '' 's/careplansApi/combinedPrescriptionsApi/g'
find . -name "*.tsx" -o -name "*.ts" | xargs sed -i '' 's/Careplan\b/Prescription/g'
find . -name "*.tsx" -o -name "*.ts" | xargs sed -i '' 's/careplan\b/combinedPrescription/g'
find . -name "*.tsx" -o -name "*.ts" | xargs sed -i '' 's/\/careplans\//\/combined-prescriptions\//g'Step 2: Create New Component Files
Patient Problems Component
// frontend/apps/cms/src/components/patient-problems/PatientProblemsList.tsx
import { useEffect, useState } from 'react';
import { patientProblemsApi } from '@kulcare/api-client';
import type { PatientProblem } from '@kulcare/api-client/types';
export function PatientProblemsList({ patientId }: { patientId: number }) {
const [problems, setProblems] = useState<PatientProblem[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadProblems();
}, [patientId]);
const loadProblems = async () => {
try {
setLoading(true);
const result = await patientProblemsApi.list({ patient_id: patientId });
setProblems(result.data);
} catch (error) {
console.error('Failed to load patient problems:', error);
} finally {
setLoading(false);
}
};
const updateProblemStatus = async (id: number, status: string) => {
try {
await patientProblemsApi.updateStatus(id, status);
loadProblems(); // Reload
} catch (error) {
console.error('Failed to update problem status:', error);
}
};
if (loading) return <div>Loading problems...</div>;
return (
<div className="patient-problems-list">
<div className="problems-header">
<h3>Active Problems</h3>
<button onClick={() => setShowCreateForm(true)}>Add Problem</button>
</div>
<div className="problems-grid">
{problems.filter(p => p.status === 'active').map(problem => (
<PatientProblemCard
key={problem.id}
problem={problem}
onStatusUpdate={updateProblemStatus}
/>
))}
</div>
</div>
);
}Phase 6: Mock Data Updates
Step 1: Update Mock Services
Update Mock Data Structure
// frontend/packages/mock-data/src/longitudinal-mock.ts
import type { PatientLongitudinalData } from '@kulcare/api-client/types';
export const mockPatientLHR: PatientLongitudinalData = {
patient: mockPatient,
current_problems: [
{
id: 1,
problem_name: 'Hypertension',
status: 'active',
severity: 'moderate',
onset_date: '2024-01-15',
// ... other fields
},
],
current_medications: [
{
id: 1,
medicine_name: 'Amlodipine',
dosage: '5mg',
frequency: 'Once daily',
active: true,
// ... other fields
},
],
encounter_history: [
{
id: 1,
appointment_date: '2024-12-01',
diagnosis: ['Hypertension follow-up'],
observations: 'Blood pressure well controlled',
// ... other fields
},
],
medication_history: [
{
id: 1,
medication_name: 'Amlodipine',
status: 'active',
dosage_instruction: '5mg once daily',
created_at: '2024-12-01T10:00:00Z',
// ... other fields
},
],
health_trends: {
vitals_timeline: mockVitalsTrends,
weight_timeline: mockWeightTrends,
medication_adherence: mockMedicationAdherence,
problem_timeline: mockProblemTrends,
},
};Phase 7: Testing Updates
Step 1: Update Component Tests
Update Test Imports
// frontend/apps/cms/src/components/combined-prescriptions/__tests__/PrescriptionForm.test.tsx
// Update imports
import { combinedPrescriptionsApi } from '@kulcare/api-client';
import type { Prescription } from '@kulcare/api-client/types';
// Update mock API calls
jest.mock('@kulcare/api-client', () => ({
combinedPrescriptionsApi: {
create: jest.fn(),
list: jest.fn(),
update: jest.fn(),
},
}));
describe('PrescriptionForm', () => {
it('creates combined prescription successfully', async () => {
const mockCreate = combinedPrescriptionsApi.create as jest.Mock;
mockCreate.mockResolvedValue({ data: mockPrescription });
// ... test implementation
});
});Step 2: Create New Component Tests
Test New LHR Components
// frontend/apps/cms/src/components/patient/__tests__/PatientLHR.test.tsx
import { render, screen, waitFor } from '@testing-library/react';
import { PatientLHR } from '../PatientLHR';
import { lhrApi } from '@kulcare/api-client';
jest.mock('@kulcare/api-client');
describe('PatientLHR', () => {
it('loads and displays longitudinal health record', async () => {
const mockGetLHR = lhrApi.getPatientLHR as jest.Mock;
mockGetLHR.mockResolvedValue({ data: mockPatientLHR });
render(<PatientLHR patientId={123} />);
await waitFor(() => {
expect(screen.getByText('Current Problems')).toBeInTheDocument();
expect(screen.getByText('Current Medications')).toBeInTheDocument();
});
});
});Migration Checklist
Backend Verification (Completed ✅)
- Database table renamed:
careplans→prescriptions - Column renamed:
encounter_id→appointment_idacross all FHIR tables - Polymorphic types updated:
'Careplan'→'Prescription' - Event tracking updated:
'careplan.create'→'prescription.create' - Model associations updated:
.careplans→.prescriptions - API endpoints created for all 6 LHR tabs
API Layer
- Create new API client files for each tab:
-
vitals.ts- For Vitals tab -
clinical-impressions.ts- For Observations tab -
medication-requests.ts- For Medications tab -
service-requests.ts- For Investigations tab -
care-plans.ts- For Advice and Next Visit tabs
-
- Update type definitions to use
appointment_idinstead ofencounter_id - Test all new API endpoints with Postman collection
- Update API client index exports
Component Layer
- Create new LHR tab components:
-
VitalsTab.tsx -
ObservationsTab.tsx(Clinical Impressions) -
MedicationsTab.tsx -
InvestigationsTab.tsx(Service Requests) -
AdviceTab.tsx -
NextVisitTab.tsx
-
- Create
LHRPrescriptionPage.tsxparent component - Create
LHRTabNavigation.tsxcomponent - Update existing careplan components to use new prescription APIs
Routing
- Implement new route structure:
/cms/prescription/patient/:patientId/:appointmentId - Add child routes for each tab (vitals, observations, medications, etc.)
- Add redirect routes for backward compatibility
- Update navigation links throughout the app
- Test route transitions and deep linking
State Management
- Create new Zustand stores for each LHR resource:
-
vitals-store.ts -
clinical-impressions-store.ts -
medication-requests-store.ts -
service-requests-store.ts -
care-plans-store.ts
-
- Update React Query keys for new endpoints
- Implement real-time updates with Ably for LHR changes
- Test state management flows between tabs
Mock Data & Development
- Update mock data to use
appointment_idinstead ofencounter_id - Create mock responses for all new FHIR endpoints
- Update development seed data
- Create development environment variables for LHR feature flags
Testing
- Update existing careplan-related tests
- Create tests for new LHR tab components
- Create integration tests for tab navigation
- Update API mock configurations
- Test error handling and loading states
- Run full test suite and ensure no regressions
Documentation & Training
- Update component documentation and Storybook stories
- Create usage examples for new LHR components
- Update developer onboarding documentation
- Create user guide for new LHR interface
- Document API endpoints and payload structures
Deployment Strategy
Development Environment
- Deploy backend changes first
- Test legacy API endpoints still work
- Deploy frontend changes with feature flags
- Gradually enable new features per user group
Production Environment
- Run database migrations during maintenance window
- Deploy backend with backward compatibility
- Deploy frontend with legacy route redirects
- Monitor error rates and rollback if needed
- Remove legacy support after successful transition
Monitoring
- Track API usage patterns for old vs new endpoints
- Monitor error rates during transition
- Set up alerts for FHIR resource creation failures
- Track document generation success rates