@medplum/dosespot-react 5.1.8 → 5.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/cjs/index.d.ts +58 -3
- package/dist/esm/index.d.ts +58 -3
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +4 -4
- package/package.json +11 -11
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var index_exports={};__export(index_exports,{DOSESPOT_ADD_FAVORITE_MEDICATION_BOT:()=>DOSESPOT_ADD_FAVORITE_MEDICATION_BOT,DOSESPOT_ADD_PATIENT_PHARMACY_BOT:()=>DOSESPOT_ADD_PATIENT_PHARMACY_BOT,DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM:()=>DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM,DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM:()=>DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM,DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT:()=>DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT,DOSESPOT_IFRAME_BOT:()=>DOSESPOT_IFRAME_BOT,DOSESPOT_MEDICATION_HISTORY_BOT:()=>DOSESPOT_MEDICATION_HISTORY_BOT,DOSESPOT_NOTIFICATION_COUNTS_BOT:()=>DOSESPOT_NOTIFICATION_COUNTS_BOT,DOSESPOT_PATIENT_ID_SYSTEM:()=>DOSESPOT_PATIENT_ID_SYSTEM,DOSESPOT_PATIENT_SYNC_BOT:()=>DOSESPOT_PATIENT_SYNC_BOT,DOSESPOT_PHARMACY_ID_SYSTEM:()=>DOSESPOT_PHARMACY_ID_SYSTEM,DOSESPOT_PRESCRIPTIONS_SYNC_BOT:()=>DOSESPOT_PRESCRIPTIONS_SYNC_BOT,DOSESPOT_SEARCH_MEDICATIONS_BOT:()=>DOSESPOT_SEARCH_MEDICATIONS_BOT,DOSESPOT_SEARCH_PHARMACY_BOT:()=>DOSESPOT_SEARCH_PHARMACY_BOT,MEDPLUM_BOT_SYSTEM:()=>MEDPLUM_BOT_SYSTEM,PATIENT_PREFERRED_PHARMACY_URL:()=>import_core3.PATIENT_PREFERRED_PHARMACY_URL,PHARMACY_PREFERENCE_TYPE_SYSTEM:()=>import_core3.PHARMACY_PREFERENCE_TYPE_SYSTEM,PHARMACY_TYPE_PREFERRED:()=>import_core3.PHARMACY_TYPE_PREFERRED,PHARMACY_TYPE_PRIMARY:()=>import_core3.PHARMACY_TYPE_PRIMARY,addPreferredPharmacyToPatient:()=>import_core3.addPreferredPharmacyToPatient,createPreferredPharmacyExtension:()=>import_core3.createPreferredPharmacyExtension,getMedicationName:()=>getMedicationName,getPharmacyIdFromOrganization:()=>getPharmacyIdFromOrganization,getPreferredPharmaciesFromPatient:()=>import_core3.getPreferredPharmaciesFromPatient,isAddPharmacyResponse:()=>import_core3.isAddPharmacyResponse,isOrganizationArray:()=>import_core3.isOrganizationArray,removePreferredPharmacyFromPatient:()=>import_core3.removePreferredPharmacyFromPatient,useDoseSpotClinicFormulary:()=>useDoseSpotClinicFormulary,useDoseSpotIFrame:()=>useDoseSpotIFrame,useDoseSpotNotifications:()=>useDoseSpotNotifications,useDoseSpotPatientPharmacy:()=>useDoseSpotPatientPharmacy,useDoseSpotPharmacySearch:()=>useDoseSpotPharmacySearch});module.exports=__toCommonJS(index_exports);var import_core=require("@medplum/core");var MEDPLUM_BOT_SYSTEM="https://www.medplum.com/bots",DOSESPOT_PATIENT_ID_SYSTEM="https://dosespot.com/patient-id",DOSESPOT_PHARMACY_ID_SYSTEM="https://dosespot.com/pharmacy-id",DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM="https://dosespot.com/clinic-favorite-medication-id",DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM="https://dosespot.com/dispensable-drug-id",DOSESPOT_SEARCH_PHARMACY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-search-pharmacy-bot"},DOSESPOT_ADD_PATIENT_PHARMACY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-add-patient-pharmacy-bot"},DOSESPOT_PATIENT_SYNC_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-patient-sync-bot"},DOSESPOT_IFRAME_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-iframe-bot"},DOSESPOT_ADD_FAVORITE_MEDICATION_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-add-favorite-medication-bot"},DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-get-favorite-medications-bot"},DOSESPOT_SEARCH_MEDICATIONS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-search-medication-bot"},DOSESPOT_MEDICATION_HISTORY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-medication-history-bot"},DOSESPOT_PRESCRIPTIONS_SYNC_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-prescriptions-sync-bot"},DOSESPOT_NOTIFICATION_COUNTS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-notification-counts-bot"};var import_core2=require("@medplum/core"),import_react_hooks=require("@medplum/react-hooks"),import_react=require("react");function useDoseSpotClinicFormulary(){let[directions,privateSetDirections]=(0,import_react.useState)(void 0),[selectedMedication,privateSetSelectedMedication]=(0,import_react.useState)(void 0),medplum=(0,import_react_hooks.useMedplum)(),state={selectedMedication,directions},saveFavoriteMedication=(0,import_react.useCallback)(async()=>{if(!selectedMedication)throw new Error("Must select a medication before adding a favorite medication");let medicationKnowledgeWithDirections={resourceType:"MedicationKnowledge",code:{...selectedMedication},administrationGuidelines:[{dosage:[{dosage:[{patientInstruction:directions||""}],type:{coding:[{system:"https://dosespot.com/patient-instructions"}]}}]}]};return medplum.executeBot(DOSESPOT_ADD_FAVORITE_MEDICATION_BOT,medicationKnowledgeWithDirections)},[selectedMedication,directions,medplum]),searchMedications=(0,import_react.useCallback)(async searchTerm=>await medplum.executeBot(DOSESPOT_SEARCH_MEDICATIONS_BOT,{name:searchTerm}),[medplum]);return{state,searchMedications,setSelectedMedication:medication=>{let medicationToSet;(0,import_core2.isCodeableConcept)(medication)?medicationToSet={...medication}:(0,import_core2.isCoding)(medication)&&(medicationToSet={text:medication.display||"",coding:[medication]}),privateSetSelectedMedication(medicationToSet)},setSelectedMedicationDirections:directions2=>{privateSetDirections(directions2)},saveFavoriteMedication,clear:()=>{privateSetSelectedMedication(void 0),privateSetDirections(void 0)}}}var import_react_hooks2=require("@medplum/react-hooks");function useDoseSpotIFrame(options){return(0,import_react_hooks2.useEPrescribingIFrame)(DOSESPOT_PATIENT_SYNC_BOT,DOSESPOT_IFRAME_BOT,options)}var import_react_hooks3=require("@medplum/react-hooks"),import_react2=require("react");var DEFAULT_REFRESH_INTERVAL_MILLISECONDS=1e4;function useDoseSpotNotifications(options){let medplum=(0,import_react_hooks3.useMedplum)(),{onChange,onError}=options??{},hasDoseSpot=medplum.getProjectMembership()?.identifier?.some(i=>i.system?.includes("dosespot")),refreshInterval=options?.refreshIntervalMilliseconds??DEFAULT_REFRESH_INTERVAL_MILLISECONDS,timerRef=(0,import_react2.useRef)(void 0),[unreadCount,setUnreadCount]=(0,import_react2.useState)(void 0),stopTimer=(0,import_react2.useCallback)(()=>{let timerId=timerRef.current;timerId&&clearInterval(timerId)},[]),updateCount=(0,import_react2.useCallback)(async()=>{try{let result=await medplum.executeBot(DOSESPOT_NOTIFICATION_COUNTS_BOT,{}),newCount=0;result.PendingPrescriptionsCount&&(newCount+=result.PendingPrescriptionsCount),result.PendingRxChangeCount&&(newCount+=result.PendingRxChangeCount),result.RefillRequestsCount&&(newCount+=result.RefillRequestsCount),result.TransactionErrorsCount&&(newCount+=result.TransactionErrorsCount),newCount!==unreadCount&&(setUnreadCount(newCount),onChange?.(newCount))}catch(err){onError?.(err),stopTimer()}},[medplum,unreadCount,onChange,onError,stopTimer]),startTimer=(0,import_react2.useCallback)(()=>{timerRef.current=setInterval(()=>{updateCount().catch(console.error)},refreshInterval)},[updateCount,refreshInterval]);return(0,import_react2.useEffect)(()=>(hasDoseSpot&&startTimer(),stopTimer),[hasDoseSpot,startTimer,stopTimer]),unreadCount}var import_react_hooks4=require("@medplum/react-hooks"),import_react3=require("react");var import_core3=require("@medplum/core"),getMedicationName=medication=>medication?.code?.text||"";function getPharmacyIdFromOrganization(organization){let id=organization.identifier?.find(i=>i.system===DOSESPOT_PHARMACY_ID_SYSTEM)?.value;return id?Number.parseInt(id,10):void 0}function useDoseSpotPatientPharmacy(){let[selectedPharmacy,privateSetSelectedPharmacy]=(0,import_react3.useState)(void 0),[setAsPrimaryState,privateSetAsPrimary]=(0,import_react3.useState)(!1),medplum=(0,import_react_hooks4.useMedplum)(),state={selectedPharmacy,setAsPrimary:setAsPrimaryState},searchPharmacies=(0,import_react3.useCallback)(async params=>await medplum.executeBot(DOSESPOT_SEARCH_PHARMACY_BOT,params),[medplum]),setSelectedPharmacy=pharmacy=>{privateSetSelectedPharmacy(pharmacy)},setAsPrimary=primary=>{privateSetAsPrimary(primary)},addFavoritePharmacy=(0,import_react3.useCallback)(async patientId=>{if(!selectedPharmacy)throw new Error("Must select a pharmacy before adding it as a favorite");if(!getPharmacyIdFromOrganization(selectedPharmacy))throw new Error("Selected pharmacy does not have a valid DoseSpot pharmacy ID");return medplum.executeBot(DOSESPOT_ADD_PATIENT_PHARMACY_BOT,{patientId,pharmacy:selectedPharmacy,setAsPrimary:setAsPrimaryState})},[selectedPharmacy,setAsPrimaryState,medplum]);return{state,searchPharmacies,setSelectedPharmacy,setAsPrimary,addFavoritePharmacy,clear:()=>{privateSetSelectedPharmacy(void 0),privateSetAsPrimary(!1)}}}var import_react_hooks5=require("@medplum/react-hooks");function useDoseSpotPharmacySearch(){return(0,import_react_hooks5.usePharmacySearch)(DOSESPOT_SEARCH_PHARMACY_BOT,DOSESPOT_ADD_PATIENT_PHARMACY_BOT)}
|
|
1
|
+
"use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var index_exports={};__export(index_exports,{DOSESPOT_ADD_FAVORITE_MEDICATION_BOT:()=>DOSESPOT_ADD_FAVORITE_MEDICATION_BOT,DOSESPOT_ADD_PATIENT_PHARMACY_BOT:()=>DOSESPOT_ADD_PATIENT_PHARMACY_BOT,DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM:()=>DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM,DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM:()=>DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM,DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT:()=>DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT,DOSESPOT_IFRAME_BOT:()=>DOSESPOT_IFRAME_BOT,DOSESPOT_MEDICATION_HISTORY_BOT:()=>DOSESPOT_MEDICATION_HISTORY_BOT,DOSESPOT_NOTIFICATION_COUNTS_BOT:()=>DOSESPOT_NOTIFICATION_COUNTS_BOT,DOSESPOT_PATIENT_ID_SYSTEM:()=>DOSESPOT_PATIENT_ID_SYSTEM,DOSESPOT_PATIENT_SYNC_BOT:()=>DOSESPOT_PATIENT_SYNC_BOT,DOSESPOT_PHARMACY_ID_SYSTEM:()=>DOSESPOT_PHARMACY_ID_SYSTEM,DOSESPOT_PRESCRIPTIONS_SYNC_BOT:()=>DOSESPOT_PRESCRIPTIONS_SYNC_BOT,DOSESPOT_SEARCH_MEDICATIONS_BOT:()=>DOSESPOT_SEARCH_MEDICATIONS_BOT,DOSESPOT_SEARCH_PHARMACY_BOT:()=>DOSESPOT_SEARCH_PHARMACY_BOT,DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT:()=>DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,MEDPLUM_BOT_SYSTEM:()=>MEDPLUM_BOT_SYSTEM,PATIENT_PREFERRED_PHARMACY_URL:()=>import_core3.PATIENT_PREFERRED_PHARMACY_URL,PHARMACY_PREFERENCE_TYPE_SYSTEM:()=>import_core3.PHARMACY_PREFERENCE_TYPE_SYSTEM,PHARMACY_TYPE_PREFERRED:()=>import_core3.PHARMACY_TYPE_PREFERRED,PHARMACY_TYPE_PRIMARY:()=>import_core3.PHARMACY_TYPE_PRIMARY,addPreferredPharmacyToPatient:()=>import_core3.addPreferredPharmacyToPatient,createPreferredPharmacyExtension:()=>import_core3.createPreferredPharmacyExtension,getMedicationName:()=>getMedicationName,getPharmacyIdFromOrganization:()=>getPharmacyIdFromOrganization,getPreferredPharmaciesFromPatient:()=>import_core3.getPreferredPharmaciesFromPatient,isAddPharmacyResponse:()=>import_core3.isAddPharmacyResponse,isOrganizationArray:()=>import_core3.isOrganizationArray,removePreferredPharmacyFromPatient:()=>import_core3.removePreferredPharmacyFromPatient,useDoseSpotClinicFormulary:()=>useDoseSpotClinicFormulary,useDoseSpotIFrame:()=>useDoseSpotIFrame,useDoseSpotNotifications:()=>useDoseSpotNotifications,useDoseSpotPatientPharmacy:()=>useDoseSpotPatientPharmacy,useDoseSpotPharmacySearch:()=>useDoseSpotPharmacySearch,useDoseSpotSelfEnrollment:()=>useDoseSpotSelfEnrollment});module.exports=__toCommonJS(index_exports);var import_core=require("@medplum/core");var MEDPLUM_BOT_SYSTEM="https://www.medplum.com/bots",DOSESPOT_PATIENT_ID_SYSTEM="https://dosespot.com/patient-id",DOSESPOT_PHARMACY_ID_SYSTEM="https://dosespot.com/pharmacy-id",DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM="https://dosespot.com/clinic-favorite-medication-id",DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM="https://dosespot.com/dispensable-drug-id",DOSESPOT_SEARCH_PHARMACY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-search-pharmacy-bot"},DOSESPOT_ADD_PATIENT_PHARMACY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-add-patient-pharmacy-bot"},DOSESPOT_PATIENT_SYNC_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-patient-sync-bot"},DOSESPOT_IFRAME_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-iframe-bot"},DOSESPOT_ADD_FAVORITE_MEDICATION_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-add-favorite-medication-bot"},DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-get-favorite-medications-bot"},DOSESPOT_SEARCH_MEDICATIONS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-search-medication-bot"},DOSESPOT_MEDICATION_HISTORY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-medication-history-bot"},DOSESPOT_PRESCRIPTIONS_SYNC_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-prescriptions-sync-bot"},DOSESPOT_NOTIFICATION_COUNTS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-notification-counts-bot"},DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-self-enroll-prescriber-bot"};var import_core2=require("@medplum/core"),import_react_hooks=require("@medplum/react-hooks"),import_react=require("react");function useDoseSpotClinicFormulary(){let[directions,privateSetDirections]=(0,import_react.useState)(void 0),[selectedMedication,privateSetSelectedMedication]=(0,import_react.useState)(void 0),medplum=(0,import_react_hooks.useMedplum)(),state={selectedMedication,directions},saveFavoriteMedication=(0,import_react.useCallback)(async()=>{if(!selectedMedication)throw new Error("Must select a medication before adding a favorite medication");let medicationKnowledgeWithDirections={resourceType:"MedicationKnowledge",code:{...selectedMedication},administrationGuidelines:[{dosage:[{dosage:[{patientInstruction:directions||""}],type:{coding:[{system:"https://dosespot.com/patient-instructions"}]}}]}]};return medplum.executeBot(DOSESPOT_ADD_FAVORITE_MEDICATION_BOT,medicationKnowledgeWithDirections)},[selectedMedication,directions,medplum]),searchMedications=(0,import_react.useCallback)(async searchTerm=>await medplum.executeBot(DOSESPOT_SEARCH_MEDICATIONS_BOT,{name:searchTerm}),[medplum]);return{state,searchMedications,setSelectedMedication:medication=>{let medicationToSet;(0,import_core2.isCodeableConcept)(medication)?medicationToSet={...medication}:(0,import_core2.isCoding)(medication)&&(medicationToSet={text:medication.display||"",coding:[medication]}),privateSetSelectedMedication(medicationToSet)},setSelectedMedicationDirections:directions2=>{privateSetDirections(directions2)},saveFavoriteMedication,clear:()=>{privateSetSelectedMedication(void 0),privateSetDirections(void 0)}}}var import_react_hooks2=require("@medplum/react-hooks"),import_react2=require("react");function useDoseSpotIFrame(options){let medplum=(0,import_react_hooks2.useMedplum)(),{patientId,selfEnroll,onPatientSyncSuccess,onIframeSuccess,onSelfEnrollSuccess,onError}=options,[iframeUrl,setIframeUrl]=(0,import_react2.useState)(void 0),onPatientSyncSuccessRef=(0,import_react2.useRef)(onPatientSyncSuccess),onIframeSuccessRef=(0,import_react2.useRef)(onIframeSuccess),onSelfEnrollSuccessRef=(0,import_react2.useRef)(onSelfEnrollSuccess),onErrorRef=(0,import_react2.useRef)(onError);return(0,import_react2.useEffect)(()=>{onPatientSyncSuccessRef.current=onPatientSyncSuccess,onIframeSuccessRef.current=onIframeSuccess,onSelfEnrollSuccessRef.current=onSelfEnrollSuccess,onErrorRef.current=onError},[onPatientSyncSuccess,onIframeSuccess,onSelfEnrollSuccess,onError]),(0,import_react2.useEffect)(()=>{let cancelled=!1;return(async()=>{if(selfEnroll&&!hasDoseSpotIdentifier(medplum.getProjectMembership())){let enrollResult=await medplum.executeBot(DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,{});if(cancelled)return;onSelfEnrollSuccessRef.current?.(enrollResult)}if(patientId){if(await medplum.executeBot(DOSESPOT_PATIENT_SYNC_BOT,{patientId}),cancelled)return;onPatientSyncSuccessRef.current?.()}let result=await medplum.executeBot(DOSESPOT_IFRAME_BOT,{patientId});cancelled||result.url&&(setIframeUrl(result.url),onIframeSuccessRef.current?.(result.url))})().catch(err=>{cancelled||onErrorRef.current?.(err)}),()=>{cancelled=!0}},[medplum,patientId,selfEnroll]),iframeUrl}function hasDoseSpotIdentifier(membership){return!!membership?.identifier?.some(i=>i.system?.includes("dosespot"))}var import_react_hooks3=require("@medplum/react-hooks"),import_react3=require("react");var DEFAULT_REFRESH_INTERVAL_MILLISECONDS=1e4;function useDoseSpotNotifications(options){let medplum=(0,import_react_hooks3.useMedplum)(),{onChange,onError}=options??{},hasDoseSpot=medplum.getProjectMembership()?.identifier?.some(i=>i.system?.includes("dosespot")),refreshInterval=options?.refreshIntervalMilliseconds??DEFAULT_REFRESH_INTERVAL_MILLISECONDS,timerRef=(0,import_react3.useRef)(void 0),[unreadCount,setUnreadCount]=(0,import_react3.useState)(void 0),stopTimer=(0,import_react3.useCallback)(()=>{let timerId=timerRef.current;timerId&&clearInterval(timerId)},[]),updateCount=(0,import_react3.useCallback)(async()=>{try{let result=await medplum.executeBot(DOSESPOT_NOTIFICATION_COUNTS_BOT,{}),newCount=0;result.PendingPrescriptionsCount&&(newCount+=result.PendingPrescriptionsCount),result.PendingRxChangeCount&&(newCount+=result.PendingRxChangeCount),result.RefillRequestsCount&&(newCount+=result.RefillRequestsCount),result.TransactionErrorsCount&&(newCount+=result.TransactionErrorsCount),newCount!==unreadCount&&(setUnreadCount(newCount),onChange?.(newCount))}catch(err){onError?.(err),stopTimer()}},[medplum,unreadCount,onChange,onError,stopTimer]),startTimer=(0,import_react3.useCallback)(()=>{timerRef.current=setInterval(()=>{updateCount().catch(console.error)},refreshInterval)},[updateCount,refreshInterval]);return(0,import_react3.useEffect)(()=>(hasDoseSpot&&startTimer(),stopTimer),[hasDoseSpot,startTimer,stopTimer]),unreadCount}var import_react_hooks4=require("@medplum/react-hooks"),import_react4=require("react");var import_core3=require("@medplum/core"),getMedicationName=medication=>medication?.code?.text||"";function getPharmacyIdFromOrganization(organization){let id=organization.identifier?.find(i=>i.system===DOSESPOT_PHARMACY_ID_SYSTEM)?.value;return id?Number.parseInt(id,10):void 0}function useDoseSpotPatientPharmacy(){let[selectedPharmacy,privateSetSelectedPharmacy]=(0,import_react4.useState)(void 0),[setAsPrimaryState,privateSetAsPrimary]=(0,import_react4.useState)(!1),medplum=(0,import_react_hooks4.useMedplum)(),state={selectedPharmacy,setAsPrimary:setAsPrimaryState},searchPharmacies=(0,import_react4.useCallback)(async params=>await medplum.executeBot(DOSESPOT_SEARCH_PHARMACY_BOT,params),[medplum]),setSelectedPharmacy=pharmacy=>{privateSetSelectedPharmacy(pharmacy)},setAsPrimary=primary=>{privateSetAsPrimary(primary)},addFavoritePharmacy=(0,import_react4.useCallback)(async patientId=>{if(!selectedPharmacy)throw new Error("Must select a pharmacy before adding it as a favorite");if(!getPharmacyIdFromOrganization(selectedPharmacy))throw new Error("Selected pharmacy does not have a valid DoseSpot pharmacy ID");return medplum.executeBot(DOSESPOT_ADD_PATIENT_PHARMACY_BOT,{patientId,pharmacy:selectedPharmacy,setAsPrimary:setAsPrimaryState})},[selectedPharmacy,setAsPrimaryState,medplum]);return{state,searchPharmacies,setSelectedPharmacy,setAsPrimary,addFavoritePharmacy,clear:()=>{privateSetSelectedPharmacy(void 0),privateSetAsPrimary(!1)}}}var import_react_hooks5=require("@medplum/react-hooks");function useDoseSpotPharmacySearch(){return(0,import_react_hooks5.usePharmacySearch)(DOSESPOT_SEARCH_PHARMACY_BOT,DOSESPOT_ADD_PATIENT_PHARMACY_BOT)}var import_react_hooks6=require("@medplum/react-hooks"),import_react5=require("react");function useDoseSpotSelfEnrollment(options){let medplum=(0,import_react_hooks6.useMedplum)(),enabled=options?.enabled??!0,initializingRef=(0,import_react5.useRef)(!1),[result,setResult]=(0,import_react5.useState)(void 0),[loading,setLoading]=(0,import_react5.useState)(enabled),[error,setError]=(0,import_react5.useState)(void 0),onSuccessRef=(0,import_react5.useRef)(options?.onSuccess);onSuccessRef.current=options?.onSuccess;let onErrorRef=(0,import_react5.useRef)(options?.onError);onErrorRef.current=options?.onError;let enroll=(0,import_react5.useCallback)(async()=>{if(!(initializingRef.current||!enabled)){initializingRef.current=!0,setLoading(!0),setError(void 0);try{let enrollResult=await medplum.executeBot(DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,{});setResult(enrollResult),onSuccessRef.current?.(enrollResult)}catch(err){setError(err),onErrorRef.current?.(err)}finally{setLoading(!1)}}},[medplum,enabled]);return(0,import_react5.useEffect)(()=>{enroll().catch(console.error)},[enroll]),{result,loading,error}}
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../src/index.ts", "../../../dosespot-core/src/pharmacy-utils.ts", "../../src/useDoseSpotClinicFormulary.ts", "../../src/useDoseSpotIFrame.ts", "../../src/useDoseSpotNotifications.ts", "../../src/useDoseSpotPatientPharmacy.ts", "../../src/utils.ts", "../../src/useDoseSpotPharmacySearch.ts"],
|
|
4
|
-
"sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nexport * from './common';\nexport * from './useDoseSpotClinicFormulary';\nexport * from './useDoseSpotIFrame';\nexport * from './useDoseSpotNotifications';\nexport * from './useDoseSpotPatientPharmacy';\nexport * from './useDoseSpotPharmacySearch';\nexport * from './utils';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Identifier } from '@medplum/fhirtypes';\n\n// DoseSpot-specific pharmacy preference type system\nexport const DOSESPOT_PHARMACY_PREFERENCE_TYPE_SYSTEM = 'https://dosespot.com/pharmacy-preference-type';\n\n// Re-export generic pharmacy utilities from @medplum/core\nexport {\n addPreferredPharmacyToPatient,\n createPreferredPharmacyExtension,\n getPreferredPharmaciesFromPatient,\n isAddPharmacyResponse,\n isOrganizationArray,\n PATIENT_PREFERRED_PHARMACY_URL,\n PHARMACY_PREFERENCE_TYPE_SYSTEM,\n PHARMACY_TYPE_PREFERRED,\n PHARMACY_TYPE_PRIMARY,\n removePreferredPharmacyFromPatient,\n} from '@medplum/core';\nexport type { AddFavoriteParams, AddPharmacyResponse, PharmacySearchParams, PreferredPharmacy } from '@medplum/core';\n\n// Bot system\nexport const MEDPLUM_BOT_SYSTEM = 'https://www.medplum.com/bots';\n\n// DoseSpot identifier systems\nexport const DOSESPOT_PATIENT_ID_SYSTEM = 'https://dosespot.com/patient-id';\nexport const DOSESPOT_PHARMACY_ID_SYSTEM = 'https://dosespot.com/pharmacy-id';\nexport const DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM = 'https://dosespot.com/clinic-favorite-medication-id';\nexport const DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM = 'https://dosespot.com/dispensable-drug-id';\n\n// Bot identifiers - Pharmacy\nexport const DOSESPOT_SEARCH_PHARMACY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-search-pharmacy-bot',\n};\n\nexport const DOSESPOT_ADD_PATIENT_PHARMACY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-add-patient-pharmacy-bot',\n};\n\n// Bot identifiers - Patient and Medications\nexport const DOSESPOT_PATIENT_SYNC_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-patient-sync-bot',\n};\n\nexport const DOSESPOT_IFRAME_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-iframe-bot',\n};\n\nexport const DOSESPOT_ADD_FAVORITE_MEDICATION_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-add-favorite-medication-bot',\n};\n\nexport const DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-get-favorite-medications-bot',\n};\n\nexport const DOSESPOT_SEARCH_MEDICATIONS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-search-medication-bot',\n};\n\nexport const DOSESPOT_MEDICATION_HISTORY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-medication-history-bot',\n};\n\nexport const DOSESPOT_PRESCRIPTIONS_SYNC_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-prescriptions-sync-bot',\n};\n\nexport const DOSESPOT_NOTIFICATION_COUNTS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-notification-counts-bot',\n};\n\n// DoseSpot notification response type\nexport interface DoseSpotNotificationCountsResponse {\n PendingPrescriptionsCount: number;\n PendingRxChangeCount: number;\n RefillRequestsCount: number;\n TransactionErrorsCount: number;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { isCodeableConcept, isCoding } from '@medplum/core';\nimport type { CodeableConcept, Coding, MedicationKnowledge } from '@medplum/fhirtypes';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useState } from 'react';\nimport { DOSESPOT_ADD_FAVORITE_MEDICATION_BOT, DOSESPOT_SEARCH_MEDICATIONS_BOT } from './common';\n\nexport interface DoseSpotClinicFormularyReturn {\n state: DoseSpotClinicFormularyState;\n /**\n * Search for DoseSpot Medications and returns array of temporary MedicationKnowledge objects that are not yet saved to the FHIR server\n */\n readonly searchMedications: (searchTerm: string) => Promise<CodeableConcept[]>;\n /**\n * Set the currently selected medication. Can be set as a CodeableConcept or a Coding, but state is always stored as a CodeableConcept\n */\n readonly setSelectedMedication: (medication: CodeableConcept | Coding | undefined) => void;\n /**\n * Set the directions for the currently selected medication\n */\n readonly setSelectedMedicationDirections: (directions: string | undefined) => void;\n /**\n * Save a DoseSpot Medication to the Clinic's favorites and returns the MedicationKnowledge object that was saved\n */\n readonly saveFavoriteMedication: () => Promise<MedicationKnowledge>;\n /**\n * Clear the state\n */\n readonly clear: () => void;\n}\n\nexport interface DoseSpotClinicFormularyState {\n selectedMedication: CodeableConcept | undefined;\n directions: string | undefined;\n}\n\nexport function useDoseSpotClinicFormulary(): DoseSpotClinicFormularyReturn {\n const [directions, privateSetDirections] = useState<string | undefined>(undefined);\n const [selectedMedication, privateSetSelectedMedication] = useState<CodeableConcept | undefined>(undefined);\n const medplum = useMedplum();\n\n const state: DoseSpotClinicFormularyState = { selectedMedication, directions };\n\n const saveFavoriteMedication = useCallback(async (): Promise<MedicationKnowledge> => {\n if (!selectedMedication) {\n throw new Error('Must select a medication before adding a favorite medication');\n }\n\n //Add the directions to the medicationKnowledge object\n const medicationKnowledgeWithDirections = {\n resourceType: 'MedicationKnowledge',\n code: { ...selectedMedication },\n administrationGuidelines: [\n {\n dosage: [\n {\n dosage: [\n {\n patientInstruction: directions || '',\n },\n ],\n type: {\n coding: [\n {\n system: 'https://dosespot.com/patient-instructions',\n },\n ],\n },\n },\n ],\n },\n ],\n };\n\n return medplum.executeBot(DOSESPOT_ADD_FAVORITE_MEDICATION_BOT, medicationKnowledgeWithDirections);\n }, [selectedMedication, directions, medplum]);\n\n const searchMedications = useCallback(\n async (searchTerm: string): Promise<CodeableConcept[]> => {\n return (await medplum.executeBot(DOSESPOT_SEARCH_MEDICATIONS_BOT, { name: searchTerm })) as CodeableConcept[];\n },\n [medplum]\n );\n\n const setSelectedMedicationDirections = (directions: string | undefined): void => {\n privateSetDirections(directions);\n };\n\n const setSelectedMedication = (medication: CodeableConcept | Coding | undefined): void => {\n let medicationToSet: CodeableConcept | undefined;\n if (isCodeableConcept(medication)) {\n medicationToSet = { ...medication };\n } else if (isCoding(medication)) {\n medicationToSet = {\n text: medication.display || '',\n coding: [medication],\n };\n }\n privateSetSelectedMedication(medicationToSet);\n };\n\n const clear = (): void => {\n privateSetSelectedMedication(undefined);\n privateSetDirections(undefined);\n };\n\n return {\n state,\n searchMedications,\n setSelectedMedication,\n setSelectedMedicationDirections,\n saveFavoriteMedication,\n clear,\n };\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { EPrescribingIFrameOptions } from '@medplum/react-hooks';\nimport { useEPrescribingIFrame } from '@medplum/react-hooks';\nimport { DOSESPOT_IFRAME_BOT, DOSESPOT_PATIENT_SYNC_BOT } from './common';\n\nexport type DoseSpotIFrameOptions = EPrescribingIFrameOptions;\n\n/**\n * React hook that syncs a patient to DoseSpot and returns the iframe URL.\n *\n * Thin wrapper around the generic {@link useEPrescribingIFrame} hook,\n * pre-configured with DoseSpot bot identifiers.\n *\n * @param options - Configuration and callback options.\n * @returns The DoseSpot iframe URL, or undefined while loading.\n */\nexport function useDoseSpotIFrame(options: DoseSpotIFrameOptions): string | undefined {\n return useEPrescribingIFrame(DOSESPOT_PATIENT_SYNC_BOT, DOSESPOT_IFRAME_BOT, options);\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { DoseSpotNotificationCountsResponse } from './common';\nimport { DOSESPOT_NOTIFICATION_COUNTS_BOT } from './common';\n\nexport interface DoseSpotNotificationsOptions {\n readonly refreshIntervalMilliseconds?: number;\n readonly onChange?: (count: number) => void;\n readonly onError?: (err: unknown) => void;\n}\n\nconst DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 10000;\n\nexport function useDoseSpotNotifications(options?: DoseSpotNotificationsOptions): number | undefined {\n const medplum = useMedplum();\n const { onChange, onError } = options ?? {};\n const hasDoseSpot = medplum.getProjectMembership()?.identifier?.some((i) => i.system?.includes('dosespot'));\n const refreshInterval = options?.refreshIntervalMilliseconds ?? DEFAULT_REFRESH_INTERVAL_MILLISECONDS;\n const timerRef = useRef<NodeJS.Timeout | undefined>(undefined);\n const [unreadCount, setUnreadCount] = useState<number | undefined>(undefined);\n\n const stopTimer = useCallback(() => {\n const timerId = timerRef.current;\n if (timerId) {\n clearInterval(timerId);\n }\n }, []);\n\n const updateCount = useCallback(async () => {\n try {\n const result = (await medplum.executeBot(\n DOSESPOT_NOTIFICATION_COUNTS_BOT,\n {}\n )) as DoseSpotNotificationCountsResponse;\n\n let newCount = 0;\n if (result.PendingPrescriptionsCount) {\n newCount += result.PendingPrescriptionsCount;\n }\n if (result.PendingRxChangeCount) {\n newCount += result.PendingRxChangeCount;\n }\n if (result.RefillRequestsCount) {\n newCount += result.RefillRequestsCount;\n }\n if (result.TransactionErrorsCount) {\n newCount += result.TransactionErrorsCount;\n }\n if (newCount !== unreadCount) {\n setUnreadCount(newCount);\n onChange?.(newCount);\n }\n } catch (err: unknown) {\n onError?.(err);\n stopTimer();\n }\n }, [medplum, unreadCount, onChange, onError, stopTimer]);\n\n const startTimer = useCallback(() => {\n timerRef.current = setInterval(() => {\n updateCount().catch(console.error);\n }, refreshInterval);\n }, [updateCount, refreshInterval]);\n\n useEffect(() => {\n // Start an interval timer to update the count every 5 seconds\n if (hasDoseSpot) {\n startTimer();\n }\n\n // Clear the interval timer when the component is unmounted\n return stopTimer;\n }, [hasDoseSpot, startTimer, stopTimer]);\n\n return unreadCount;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { Organization } from '@medplum/fhirtypes';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useState } from 'react';\nimport { DOSESPOT_ADD_PATIENT_PHARMACY_BOT, DOSESPOT_SEARCH_PHARMACY_BOT } from './common';\nimport { getPharmacyIdFromOrganization } from './utils';\n\n/**\n * Search parameters for pharmacy search.\n * See DoseSpot API section 3.13.2.\n */\nexport interface PharmacySearchParams {\n /** Pharmacy's store name (min 3 chars) */\n name?: string;\n /** City (min 3 chars) */\n city?: string;\n /** State (min 3 chars) */\n state?: string;\n /** Zip code (min 3 chars) */\n zip?: string;\n /** Address (min 3 chars) */\n address?: string;\n /** Phone or fax number */\n phoneOrFax?: string;\n /** Collection of pharmacy specialties (numeric values) */\n specialty?: number[];\n /** National Council for Prescription Drug Programs Identifier */\n ncpdpID?: string;\n /** Page number of results (defaults to 1) */\n pageNumber?: number;\n}\n\n/**\n * Response from adding a pharmacy to a patient's favorites.\n */\nexport interface AddPatientPharmacyResponse {\n /** Whether the operation was successful */\n success: boolean;\n /** A message describing the result */\n message: string;\n /** The persisted Organization resource with ID */\n organization?: Organization;\n}\n\n/**\n * State managed by the useDoseSpotPatientPharmacy hook.\n */\nexport interface DoseSpotPatientPharmacyState {\n /** The currently selected pharmacy Organization */\n selectedPharmacy: Organization | undefined;\n /** Whether to set the selected pharmacy as the patient's primary pharmacy */\n setAsPrimary: boolean;\n}\n\n/**\n * Return type for the useDoseSpotPatientPharmacy hook.\n */\nexport interface DoseSpotPatientPharmacyReturn {\n /** Current state of the hook */\n state: DoseSpotPatientPharmacyState;\n /**\n * Search for pharmacies in DoseSpot.\n * Returns synthetic Organization resources (not persisted to DB).\n */\n readonly searchPharmacies: (params: PharmacySearchParams) => Promise<Organization[]>;\n /**\n * Set the currently selected pharmacy.\n */\n readonly setSelectedPharmacy: (pharmacy: Organization | undefined) => void;\n /**\n * Set whether to add the pharmacy as the patient's primary pharmacy.\n */\n readonly setAsPrimary: (primary: boolean) => void;\n /**\n * Add the selected pharmacy to the patient's favorites in DoseSpot.\n * @param patientId - The Medplum Patient ID\n */\n readonly addFavoritePharmacy: (patientId: string) => Promise<AddPatientPharmacyResponse>;\n /**\n * Clear the state (selected pharmacy and setAsPrimary flag).\n */\n readonly clear: () => void;\n}\n\n/**\n * React hook for searching DoseSpot pharmacies and adding them to patient favorites.\n *\n * @returns The hook return object with state and methods.\n *\n * @example\n * ```tsx\n * function PharmacySearch({ patientId }: { patientId: string }) {\n * const {\n * state,\n * searchPharmacies,\n * setSelectedPharmacy,\n * setAsPrimary,\n * addFavoritePharmacy,\n * clear\n * } = useDoseSpotPatientPharmacy();\n *\n * const handleSearch = async () => {\n * const results = await searchPharmacies({ zip: '94118' });\n * // Display results to user\n * };\n *\n * const handleAddFavorite = async () => {\n * const result = await addFavoritePharmacy(patientId);\n * if (result.success) {\n * clear();\n * }\n * };\n * }\n * ```\n */\nexport function useDoseSpotPatientPharmacy(): DoseSpotPatientPharmacyReturn {\n const [selectedPharmacy, privateSetSelectedPharmacy] = useState<Organization | undefined>(undefined);\n const [setAsPrimaryState, privateSetAsPrimary] = useState(false);\n const medplum = useMedplum();\n\n const state: DoseSpotPatientPharmacyState = {\n selectedPharmacy,\n setAsPrimary: setAsPrimaryState,\n };\n\n const searchPharmacies = useCallback(\n async (params: PharmacySearchParams): Promise<Organization[]> => {\n return (await medplum.executeBot(DOSESPOT_SEARCH_PHARMACY_BOT, params)) as Organization[];\n },\n [medplum]\n );\n\n const setSelectedPharmacy = (pharmacy: Organization | undefined): void => {\n privateSetSelectedPharmacy(pharmacy);\n };\n\n const setAsPrimary = (primary: boolean): void => {\n privateSetAsPrimary(primary);\n };\n\n const addFavoritePharmacy = useCallback(\n async (patientId: string): Promise<AddPatientPharmacyResponse> => {\n if (!selectedPharmacy) {\n throw new Error('Must select a pharmacy before adding it as a favorite');\n }\n\n const pharmacyId = getPharmacyIdFromOrganization(selectedPharmacy);\n if (!pharmacyId) {\n throw new Error('Selected pharmacy does not have a valid DoseSpot pharmacy ID');\n }\n\n return medplum.executeBot(DOSESPOT_ADD_PATIENT_PHARMACY_BOT, {\n patientId,\n pharmacy: selectedPharmacy,\n setAsPrimary: setAsPrimaryState,\n }) as Promise<AddPatientPharmacyResponse>;\n },\n [selectedPharmacy, setAsPrimaryState, medplum]\n );\n\n const clear = (): void => {\n privateSetSelectedPharmacy(undefined);\n privateSetAsPrimary(false);\n };\n\n return {\n state,\n searchPharmacies,\n setSelectedPharmacy,\n setAsPrimary,\n addFavoritePharmacy,\n clear,\n };\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { MedicationKnowledge, Organization } from '@medplum/fhirtypes';\nimport { DOSESPOT_PHARMACY_ID_SYSTEM } from './common';\n\nexport const getMedicationName = (medication: MedicationKnowledge | undefined): string => {\n return medication?.code?.text || '';\n};\n\n/**\n * Extracts the DoseSpot pharmacy ID from an Organization resource.\n * @param organization - The FHIR Organization resource representing a pharmacy.\n * @returns The DoseSpot pharmacy ID, or undefined if not found.\n */\nexport function getPharmacyIdFromOrganization(organization: Organization): number | undefined {\n const id = organization.identifier?.find((i) => i.system === DOSESPOT_PHARMACY_ID_SYSTEM)?.value;\n return id ? Number.parseInt(id, 10) : undefined;\n}\n\nexport {\n addPreferredPharmacyToPatient,\n createPreferredPharmacyExtension,\n getPreferredPharmaciesFromPatient,\n isAddPharmacyResponse,\n isOrganizationArray,\n PATIENT_PREFERRED_PHARMACY_URL,\n PHARMACY_PREFERENCE_TYPE_SYSTEM,\n PHARMACY_TYPE_PREFERRED,\n PHARMACY_TYPE_PRIMARY,\n removePreferredPharmacyFromPatient,\n} from '@medplum/core';\nexport type { PreferredPharmacy } from '@medplum/core';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { UsePharmacySearchReturn } from '@medplum/react-hooks';\nimport { usePharmacySearch } from '@medplum/react-hooks';\nimport { DOSESPOT_ADD_PATIENT_PHARMACY_BOT, DOSESPOT_SEARCH_PHARMACY_BOT } from './common';\n\nexport type UseDoseSpotPharmacySearchReturn = UsePharmacySearchReturn;\n\n/**\n * React hook that provides DoseSpot-specific pharmacy search and add-to-favorites functionality.\n *\n * Thin wrapper around the generic {@link usePharmacySearch} hook,\n * pre-configured with DoseSpot bot identifiers.\n *\n * @returns An object with `searchPharmacies` and `addToFavorites` callbacks.\n */\nexport function useDoseSpotPharmacySearch(): UseDoseSpotPharmacySearchReturn {\n return usePharmacySearch(DOSESPOT_SEARCH_PHARMACY_BOT, DOSESPOT_ADD_PATIENT_PHARMACY_BOT);\n}\n"],
|
|
5
|
-
"mappings": "wpBAAA,
|
|
6
|
-
"names": ["import_core", "directions", "import_react_hooks", "import_react_hooks", "import_react", "import_react_hooks", "import_react", "import_core", "import_react_hooks"]
|
|
3
|
+
"sources": ["../../src/index.ts", "../../../dosespot-core/src/pharmacy-utils.ts", "../../src/useDoseSpotClinicFormulary.ts", "../../src/useDoseSpotIFrame.ts", "../../src/useDoseSpotNotifications.ts", "../../src/useDoseSpotPatientPharmacy.ts", "../../src/utils.ts", "../../src/useDoseSpotPharmacySearch.ts", "../../src/useDoseSpotSelfEnrollment.ts"],
|
|
4
|
+
"sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nexport * from './common';\nexport * from './useDoseSpotClinicFormulary';\nexport * from './useDoseSpotIFrame';\nexport * from './useDoseSpotNotifications';\nexport * from './useDoseSpotPatientPharmacy';\nexport * from './useDoseSpotPharmacySearch';\nexport * from './useDoseSpotSelfEnrollment';\nexport * from './utils';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Identifier } from '@medplum/fhirtypes';\n\n// DoseSpot-specific pharmacy preference type system\nexport const DOSESPOT_PHARMACY_PREFERENCE_TYPE_SYSTEM = 'https://dosespot.com/pharmacy-preference-type';\n\n// Re-export generic pharmacy utilities from @medplum/core\nexport {\n addPreferredPharmacyToPatient,\n createPreferredPharmacyExtension,\n getPreferredPharmaciesFromPatient,\n isAddPharmacyResponse,\n isOrganizationArray,\n PATIENT_PREFERRED_PHARMACY_URL,\n PHARMACY_PREFERENCE_TYPE_SYSTEM,\n PHARMACY_TYPE_PREFERRED,\n PHARMACY_TYPE_PRIMARY,\n removePreferredPharmacyFromPatient,\n} from '@medplum/core';\nexport type { AddFavoriteParams, AddPharmacyResponse, PharmacySearchParams, PreferredPharmacy } from '@medplum/core';\n\n// Bot system\nexport const MEDPLUM_BOT_SYSTEM = 'https://www.medplum.com/bots';\n\n// DoseSpot identifier systems\nexport const DOSESPOT_PATIENT_ID_SYSTEM = 'https://dosespot.com/patient-id';\nexport const DOSESPOT_PHARMACY_ID_SYSTEM = 'https://dosespot.com/pharmacy-id';\nexport const DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM = 'https://dosespot.com/clinic-favorite-medication-id';\nexport const DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM = 'https://dosespot.com/dispensable-drug-id';\n\n// Bot identifiers - Pharmacy\nexport const DOSESPOT_SEARCH_PHARMACY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-search-pharmacy-bot',\n};\n\nexport const DOSESPOT_ADD_PATIENT_PHARMACY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-add-patient-pharmacy-bot',\n};\n\n// Bot identifiers - Patient and Medications\nexport const DOSESPOT_PATIENT_SYNC_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-patient-sync-bot',\n};\n\nexport const DOSESPOT_IFRAME_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-iframe-bot',\n};\n\nexport const DOSESPOT_ADD_FAVORITE_MEDICATION_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-add-favorite-medication-bot',\n};\n\nexport const DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-get-favorite-medications-bot',\n};\n\nexport const DOSESPOT_SEARCH_MEDICATIONS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-search-medication-bot',\n};\n\nexport const DOSESPOT_MEDICATION_HISTORY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-medication-history-bot',\n};\n\nexport const DOSESPOT_PRESCRIPTIONS_SYNC_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-prescriptions-sync-bot',\n};\n\nexport const DOSESPOT_NOTIFICATION_COUNTS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-notification-counts-bot',\n};\n\n// Bot identifiers - Enrollment\nexport const DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-self-enroll-prescriber-bot',\n};\n\n// DoseSpot notification response type\nexport interface DoseSpotNotificationCountsResponse {\n PendingPrescriptionsCount: number;\n PendingRxChangeCount: number;\n RefillRequestsCount: number;\n TransactionErrorsCount: number;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { isCodeableConcept, isCoding } from '@medplum/core';\nimport type { CodeableConcept, Coding, MedicationKnowledge } from '@medplum/fhirtypes';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useState } from 'react';\nimport { DOSESPOT_ADD_FAVORITE_MEDICATION_BOT, DOSESPOT_SEARCH_MEDICATIONS_BOT } from './common';\n\nexport interface DoseSpotClinicFormularyReturn {\n state: DoseSpotClinicFormularyState;\n /**\n * Search for DoseSpot Medications and returns array of temporary MedicationKnowledge objects that are not yet saved to the FHIR server\n */\n readonly searchMedications: (searchTerm: string) => Promise<CodeableConcept[]>;\n /**\n * Set the currently selected medication. Can be set as a CodeableConcept or a Coding, but state is always stored as a CodeableConcept\n */\n readonly setSelectedMedication: (medication: CodeableConcept | Coding | undefined) => void;\n /**\n * Set the directions for the currently selected medication\n */\n readonly setSelectedMedicationDirections: (directions: string | undefined) => void;\n /**\n * Save a DoseSpot Medication to the Clinic's favorites and returns the MedicationKnowledge object that was saved\n */\n readonly saveFavoriteMedication: () => Promise<MedicationKnowledge>;\n /**\n * Clear the state\n */\n readonly clear: () => void;\n}\n\nexport interface DoseSpotClinicFormularyState {\n selectedMedication: CodeableConcept | undefined;\n directions: string | undefined;\n}\n\nexport function useDoseSpotClinicFormulary(): DoseSpotClinicFormularyReturn {\n const [directions, privateSetDirections] = useState<string | undefined>(undefined);\n const [selectedMedication, privateSetSelectedMedication] = useState<CodeableConcept | undefined>(undefined);\n const medplum = useMedplum();\n\n const state: DoseSpotClinicFormularyState = { selectedMedication, directions };\n\n const saveFavoriteMedication = useCallback(async (): Promise<MedicationKnowledge> => {\n if (!selectedMedication) {\n throw new Error('Must select a medication before adding a favorite medication');\n }\n\n //Add the directions to the medicationKnowledge object\n const medicationKnowledgeWithDirections = {\n resourceType: 'MedicationKnowledge',\n code: { ...selectedMedication },\n administrationGuidelines: [\n {\n dosage: [\n {\n dosage: [\n {\n patientInstruction: directions || '',\n },\n ],\n type: {\n coding: [\n {\n system: 'https://dosespot.com/patient-instructions',\n },\n ],\n },\n },\n ],\n },\n ],\n };\n\n return medplum.executeBot(DOSESPOT_ADD_FAVORITE_MEDICATION_BOT, medicationKnowledgeWithDirections);\n }, [selectedMedication, directions, medplum]);\n\n const searchMedications = useCallback(\n async (searchTerm: string): Promise<CodeableConcept[]> => {\n return (await medplum.executeBot(DOSESPOT_SEARCH_MEDICATIONS_BOT, { name: searchTerm })) as CodeableConcept[];\n },\n [medplum]\n );\n\n const setSelectedMedicationDirections = (directions: string | undefined): void => {\n privateSetDirections(directions);\n };\n\n const setSelectedMedication = (medication: CodeableConcept | Coding | undefined): void => {\n let medicationToSet: CodeableConcept | undefined;\n if (isCodeableConcept(medication)) {\n medicationToSet = { ...medication };\n } else if (isCoding(medication)) {\n medicationToSet = {\n text: medication.display || '',\n coding: [medication],\n };\n }\n privateSetSelectedMedication(medicationToSet);\n };\n\n const clear = (): void => {\n privateSetSelectedMedication(undefined);\n privateSetDirections(undefined);\n };\n\n return {\n state,\n searchMedications,\n setSelectedMedication,\n setSelectedMedicationDirections,\n saveFavoriteMedication,\n clear,\n };\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { ProjectMembership } from '@medplum/fhirtypes';\nimport type { EPrescribingIFrameOptions } from '@medplum/react-hooks';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useEffect, useRef, useState } from 'react';\nimport { DOSESPOT_IFRAME_BOT, DOSESPOT_PATIENT_SYNC_BOT, DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT } from './common';\nimport type { DoseSpotSelfEnrollmentResult } from './useDoseSpotSelfEnrollment';\n\nexport interface DoseSpotIFrameOptions extends EPrescribingIFrameOptions {\n /**\n * When true, automatically runs the self-enrollment bot before loading\n * the iframe if the current user does not have a DoseSpot identifier\n * on their ProjectMembership. Requires an active PractitionerRole with\n * DoseSpot role type codes for the practitioner.\n */\n readonly selfEnroll?: boolean;\n /** Called after self-enrollment completes successfully. */\n readonly onSelfEnrollSuccess?: (result: DoseSpotSelfEnrollmentResult) => void;\n}\n\n/**\n * React hook that syncs a patient to DoseSpot and returns the iframe URL.\n *\n * Runs optional self-enrollment, then the patient-sync bot (when `patientId`\n * is set), then the iframe bot \u2014 aligned with {@link useEPrescribingIFrame}\n * behavior plus DoseSpot-specific enrollment.\n *\n * @param options - Configuration and callback options.\n * @returns The DoseSpot iframe URL, or undefined while loading.\n */\nexport function useDoseSpotIFrame(options: DoseSpotIFrameOptions): string | undefined {\n const medplum = useMedplum();\n const { patientId, selfEnroll, onPatientSyncSuccess, onIframeSuccess, onSelfEnrollSuccess, onError } = options;\n const [iframeUrl, setIframeUrl] = useState<string | undefined>(undefined);\n\n const onPatientSyncSuccessRef = useRef(onPatientSyncSuccess);\n const onIframeSuccessRef = useRef(onIframeSuccess);\n const onSelfEnrollSuccessRef = useRef(onSelfEnrollSuccess);\n const onErrorRef = useRef(onError);\n\n useEffect(() => {\n onPatientSyncSuccessRef.current = onPatientSyncSuccess;\n onIframeSuccessRef.current = onIframeSuccess;\n onSelfEnrollSuccessRef.current = onSelfEnrollSuccess;\n onErrorRef.current = onError;\n }, [onPatientSyncSuccess, onIframeSuccess, onSelfEnrollSuccess, onError]);\n\n useEffect(() => {\n let cancelled = false;\n\n const run = async (): Promise<void> => {\n if (selfEnroll && !hasDoseSpotIdentifier(medplum.getProjectMembership())) {\n const enrollResult = (await medplum.executeBot(\n DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,\n {}\n )) as DoseSpotSelfEnrollmentResult;\n if (cancelled) {\n return;\n }\n onSelfEnrollSuccessRef.current?.(enrollResult);\n }\n\n if (patientId) {\n await medplum.executeBot(DOSESPOT_PATIENT_SYNC_BOT, { patientId });\n if (cancelled) {\n return;\n }\n onPatientSyncSuccessRef.current?.();\n }\n const result = await medplum.executeBot(DOSESPOT_IFRAME_BOT, { patientId });\n if (cancelled) {\n return;\n }\n if (result.url) {\n setIframeUrl(result.url);\n onIframeSuccessRef.current?.(result.url);\n }\n };\n\n run().catch((err: unknown) => {\n if (!cancelled) {\n onErrorRef.current?.(err);\n }\n });\n\n return (): void => {\n cancelled = true;\n };\n }, [medplum, patientId, selfEnroll]);\n\n return iframeUrl;\n}\n\n/**\n * Checks whether a ProjectMembership has a DoseSpot identifier.\n *\n * @param membership - The project membership to check.\n * @returns True when membership identifiers include a DoseSpot system URL.\n */\nfunction hasDoseSpotIdentifier(membership: ProjectMembership | undefined): boolean {\n return !!membership?.identifier?.some((i) => i.system?.includes('dosespot'));\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { DoseSpotNotificationCountsResponse } from './common';\nimport { DOSESPOT_NOTIFICATION_COUNTS_BOT } from './common';\n\nexport interface DoseSpotNotificationsOptions {\n readonly refreshIntervalMilliseconds?: number;\n readonly onChange?: (count: number) => void;\n readonly onError?: (err: unknown) => void;\n}\n\nconst DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 10000;\n\nexport function useDoseSpotNotifications(options?: DoseSpotNotificationsOptions): number | undefined {\n const medplum = useMedplum();\n const { onChange, onError } = options ?? {};\n const hasDoseSpot = medplum.getProjectMembership()?.identifier?.some((i) => i.system?.includes('dosespot'));\n const refreshInterval = options?.refreshIntervalMilliseconds ?? DEFAULT_REFRESH_INTERVAL_MILLISECONDS;\n const timerRef = useRef<NodeJS.Timeout | undefined>(undefined);\n const [unreadCount, setUnreadCount] = useState<number | undefined>(undefined);\n\n const stopTimer = useCallback(() => {\n const timerId = timerRef.current;\n if (timerId) {\n clearInterval(timerId);\n }\n }, []);\n\n const updateCount = useCallback(async () => {\n try {\n const result = (await medplum.executeBot(\n DOSESPOT_NOTIFICATION_COUNTS_BOT,\n {}\n )) as DoseSpotNotificationCountsResponse;\n\n let newCount = 0;\n if (result.PendingPrescriptionsCount) {\n newCount += result.PendingPrescriptionsCount;\n }\n if (result.PendingRxChangeCount) {\n newCount += result.PendingRxChangeCount;\n }\n if (result.RefillRequestsCount) {\n newCount += result.RefillRequestsCount;\n }\n if (result.TransactionErrorsCount) {\n newCount += result.TransactionErrorsCount;\n }\n if (newCount !== unreadCount) {\n setUnreadCount(newCount);\n onChange?.(newCount);\n }\n } catch (err: unknown) {\n onError?.(err);\n stopTimer();\n }\n }, [medplum, unreadCount, onChange, onError, stopTimer]);\n\n const startTimer = useCallback(() => {\n timerRef.current = setInterval(() => {\n updateCount().catch(console.error);\n }, refreshInterval);\n }, [updateCount, refreshInterval]);\n\n useEffect(() => {\n // Start an interval timer to update the count every 5 seconds\n if (hasDoseSpot) {\n startTimer();\n }\n\n // Clear the interval timer when the component is unmounted\n return stopTimer;\n }, [hasDoseSpot, startTimer, stopTimer]);\n\n return unreadCount;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { Organization } from '@medplum/fhirtypes';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useState } from 'react';\nimport { DOSESPOT_ADD_PATIENT_PHARMACY_BOT, DOSESPOT_SEARCH_PHARMACY_BOT } from './common';\nimport { getPharmacyIdFromOrganization } from './utils';\n\n/**\n * Search parameters for pharmacy search.\n * See DoseSpot API section 3.13.2.\n */\nexport interface PharmacySearchParams {\n /** Pharmacy's store name (min 3 chars) */\n name?: string;\n /** City (min 3 chars) */\n city?: string;\n /** State (min 3 chars) */\n state?: string;\n /** Zip code (min 3 chars) */\n zip?: string;\n /** Address (min 3 chars) */\n address?: string;\n /** Phone or fax number */\n phoneOrFax?: string;\n /** Collection of pharmacy specialties (numeric values) */\n specialty?: number[];\n /** National Council for Prescription Drug Programs Identifier */\n ncpdpID?: string;\n /** Page number of results (defaults to 1) */\n pageNumber?: number;\n}\n\n/**\n * Response from adding a pharmacy to a patient's favorites.\n */\nexport interface AddPatientPharmacyResponse {\n /** Whether the operation was successful */\n success: boolean;\n /** A message describing the result */\n message: string;\n /** The persisted Organization resource with ID */\n organization?: Organization;\n}\n\n/**\n * State managed by the useDoseSpotPatientPharmacy hook.\n */\nexport interface DoseSpotPatientPharmacyState {\n /** The currently selected pharmacy Organization */\n selectedPharmacy: Organization | undefined;\n /** Whether to set the selected pharmacy as the patient's primary pharmacy */\n setAsPrimary: boolean;\n}\n\n/**\n * Return type for the useDoseSpotPatientPharmacy hook.\n */\nexport interface DoseSpotPatientPharmacyReturn {\n /** Current state of the hook */\n state: DoseSpotPatientPharmacyState;\n /**\n * Search for pharmacies in DoseSpot.\n * Returns synthetic Organization resources (not persisted to DB).\n */\n readonly searchPharmacies: (params: PharmacySearchParams) => Promise<Organization[]>;\n /**\n * Set the currently selected pharmacy.\n */\n readonly setSelectedPharmacy: (pharmacy: Organization | undefined) => void;\n /**\n * Set whether to add the pharmacy as the patient's primary pharmacy.\n */\n readonly setAsPrimary: (primary: boolean) => void;\n /**\n * Add the selected pharmacy to the patient's favorites in DoseSpot.\n * @param patientId - The Medplum Patient ID\n */\n readonly addFavoritePharmacy: (patientId: string) => Promise<AddPatientPharmacyResponse>;\n /**\n * Clear the state (selected pharmacy and setAsPrimary flag).\n */\n readonly clear: () => void;\n}\n\n/**\n * React hook for searching DoseSpot pharmacies and adding them to patient favorites.\n *\n * @returns The hook return object with state and methods.\n *\n * @example\n * ```tsx\n * function PharmacySearch({ patientId }: { patientId: string }) {\n * const {\n * state,\n * searchPharmacies,\n * setSelectedPharmacy,\n * setAsPrimary,\n * addFavoritePharmacy,\n * clear\n * } = useDoseSpotPatientPharmacy();\n *\n * const handleSearch = async () => {\n * const results = await searchPharmacies({ zip: '94118' });\n * // Display results to user\n * };\n *\n * const handleAddFavorite = async () => {\n * const result = await addFavoritePharmacy(patientId);\n * if (result.success) {\n * clear();\n * }\n * };\n * }\n * ```\n */\nexport function useDoseSpotPatientPharmacy(): DoseSpotPatientPharmacyReturn {\n const [selectedPharmacy, privateSetSelectedPharmacy] = useState<Organization | undefined>(undefined);\n const [setAsPrimaryState, privateSetAsPrimary] = useState(false);\n const medplum = useMedplum();\n\n const state: DoseSpotPatientPharmacyState = {\n selectedPharmacy,\n setAsPrimary: setAsPrimaryState,\n };\n\n const searchPharmacies = useCallback(\n async (params: PharmacySearchParams): Promise<Organization[]> => {\n return (await medplum.executeBot(DOSESPOT_SEARCH_PHARMACY_BOT, params)) as Organization[];\n },\n [medplum]\n );\n\n const setSelectedPharmacy = (pharmacy: Organization | undefined): void => {\n privateSetSelectedPharmacy(pharmacy);\n };\n\n const setAsPrimary = (primary: boolean): void => {\n privateSetAsPrimary(primary);\n };\n\n const addFavoritePharmacy = useCallback(\n async (patientId: string): Promise<AddPatientPharmacyResponse> => {\n if (!selectedPharmacy) {\n throw new Error('Must select a pharmacy before adding it as a favorite');\n }\n\n const pharmacyId = getPharmacyIdFromOrganization(selectedPharmacy);\n if (!pharmacyId) {\n throw new Error('Selected pharmacy does not have a valid DoseSpot pharmacy ID');\n }\n\n return medplum.executeBot(DOSESPOT_ADD_PATIENT_PHARMACY_BOT, {\n patientId,\n pharmacy: selectedPharmacy,\n setAsPrimary: setAsPrimaryState,\n }) as Promise<AddPatientPharmacyResponse>;\n },\n [selectedPharmacy, setAsPrimaryState, medplum]\n );\n\n const clear = (): void => {\n privateSetSelectedPharmacy(undefined);\n privateSetAsPrimary(false);\n };\n\n return {\n state,\n searchPharmacies,\n setSelectedPharmacy,\n setAsPrimary,\n addFavoritePharmacy,\n clear,\n };\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { MedicationKnowledge, Organization } from '@medplum/fhirtypes';\nimport { DOSESPOT_PHARMACY_ID_SYSTEM } from './common';\n\nexport const getMedicationName = (medication: MedicationKnowledge | undefined): string => {\n return medication?.code?.text || '';\n};\n\n/**\n * Extracts the DoseSpot pharmacy ID from an Organization resource.\n * @param organization - The FHIR Organization resource representing a pharmacy.\n * @returns The DoseSpot pharmacy ID, or undefined if not found.\n */\nexport function getPharmacyIdFromOrganization(organization: Organization): number | undefined {\n const id = organization.identifier?.find((i) => i.system === DOSESPOT_PHARMACY_ID_SYSTEM)?.value;\n return id ? Number.parseInt(id, 10) : undefined;\n}\n\nexport {\n addPreferredPharmacyToPatient,\n createPreferredPharmacyExtension,\n getPreferredPharmaciesFromPatient,\n isAddPharmacyResponse,\n isOrganizationArray,\n PATIENT_PREFERRED_PHARMACY_URL,\n PHARMACY_PREFERENCE_TYPE_SYSTEM,\n PHARMACY_TYPE_PREFERRED,\n PHARMACY_TYPE_PRIMARY,\n removePreferredPharmacyFromPatient,\n} from '@medplum/core';\nexport type { PreferredPharmacy } from '@medplum/core';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { UsePharmacySearchReturn } from '@medplum/react-hooks';\nimport { usePharmacySearch } from '@medplum/react-hooks';\nimport { DOSESPOT_ADD_PATIENT_PHARMACY_BOT, DOSESPOT_SEARCH_PHARMACY_BOT } from './common';\n\nexport type UseDoseSpotPharmacySearchReturn = UsePharmacySearchReturn;\n\n/**\n * React hook that provides DoseSpot-specific pharmacy search and add-to-favorites functionality.\n *\n * Thin wrapper around the generic {@link usePharmacySearch} hook,\n * pre-configured with DoseSpot bot identifiers.\n *\n * @returns An object with `searchPharmacies` and `addToFavorites` callbacks.\n */\nexport function useDoseSpotPharmacySearch(): UseDoseSpotPharmacySearchReturn {\n return usePharmacySearch(DOSESPOT_SEARCH_PHARMACY_BOT, DOSESPOT_ADD_PATIENT_PHARMACY_BOT);\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT } from './common';\n\n/**\n * Result returned by the DoseSpot self-enrollment bot.\n */\nexport interface DoseSpotSelfEnrollmentResult {\n status: 'created' | 'already_enrolled' | 'advanced';\n doseSpotClinicianId: number;\n registrationStatus: string;\n epcsEnabled: boolean;\n nextSteps: string[];\n}\n\n/**\n * Options for the {@link useDoseSpotSelfEnrollment} hook.\n */\nexport interface DoseSpotSelfEnrollmentOptions {\n /** Whether to run the self-enrollment bot. Defaults to true. */\n readonly enabled?: boolean;\n /** Called when enrollment succeeds. */\n readonly onSuccess?: (result: DoseSpotSelfEnrollmentResult) => void;\n /** Called when enrollment fails. */\n readonly onError?: (err: unknown) => void;\n}\n\n/**\n * React hook that executes the DoseSpot self-enrollment bot.\n *\n * Runs the `dosespot-self-enroll-prescriber-bot` once on mount (when enabled)\n * and returns the enrollment result, loading state, and any error.\n *\n * The bot is idempotent -- it creates the clinician on the first call and\n * auto-advances through registration stages (IDP, TFA) on subsequent calls.\n *\n * @param options - Configuration options.\n * @returns An object with `result`, `loading`, and `error` fields.\n */\nexport function useDoseSpotSelfEnrollment(options?: DoseSpotSelfEnrollmentOptions): {\n result: DoseSpotSelfEnrollmentResult | undefined;\n loading: boolean;\n error: unknown;\n} {\n const medplum = useMedplum();\n const enabled = options?.enabled ?? true;\n const initializingRef = useRef(false);\n const [result, setResult] = useState<DoseSpotSelfEnrollmentResult | undefined>(undefined);\n const [loading, setLoading] = useState(enabled);\n const [error, setError] = useState<unknown>(undefined);\n\n const onSuccessRef = useRef(options?.onSuccess);\n onSuccessRef.current = options?.onSuccess;\n\n const onErrorRef = useRef(options?.onError);\n onErrorRef.current = options?.onError;\n\n const enroll = useCallback(async () => {\n if (initializingRef.current || !enabled) {\n return;\n }\n\n initializingRef.current = true;\n setLoading(true);\n setError(undefined);\n\n try {\n const enrollResult = (await medplum.executeBot(\n DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,\n {}\n )) as DoseSpotSelfEnrollmentResult;\n\n setResult(enrollResult);\n onSuccessRef.current?.(enrollResult);\n } catch (err: unknown) {\n setError(err);\n onErrorRef.current?.(err);\n } finally {\n setLoading(false);\n }\n }, [medplum, enabled]);\n\n useEffect(() => {\n enroll().catch(console.error);\n }, [enroll]);\n\n return { result, loading, error };\n}\n"],
|
|
5
|
+
"mappings": "wpBAAA,gwECSA,gBAWO,yBAdA,IAkBM,mBAAqB,+BAGrB,2BAA6B,kCAC7B,4BAA8B,mCAC9B,mCAAqC,qDACrC,oCAAsC,2CAGtC,6BAA2C,CACtD,OAAQ,mBACR,MAAO,8BACT,EAEa,kCAAgD,CAC3D,OAAQ,mBACR,MAAO,mCACT,EAGa,0BAAwC,CACnD,OAAQ,mBACR,MAAO,2BACT,EAEa,oBAAkC,CAC7C,OAAQ,mBACR,MAAO,qBACT,EAEa,qCAAmD,CAC9D,OAAQ,mBACR,MAAO,sCACT,EAEa,sCAAoD,CAC/D,OAAQ,mBACR,MAAO,uCACT,EAEa,gCAA8C,CACzD,OAAQ,mBACR,MAAO,gCACT,EAEa,gCAA8C,CACzD,OAAQ,mBACR,MAAO,iCACT,EAEa,gCAA8C,CACzD,OAAQ,mBACR,MAAO,iCACT,EAEa,iCAA+C,CAC1D,OAAQ,mBACR,MAAO,kCACT,EAGa,oCAAkD,CAC7D,OAAQ,mBACR,MAAO,qCACT,ECtFA,IAAAA,aAA4C,yBAE5C,mBAA2B,gCAC3B,aAAsC,iBAgC/B,SAAS,4BAA4D,CAC1E,GAAM,CAAC,WAAY,oBAAoB,KAAI,uBAA6B,MAAS,EAC3E,CAAC,mBAAoB,4BAA4B,KAAI,uBAAsC,MAAS,EACpG,WAAU,+BAAW,EAErB,MAAsC,CAAE,mBAAoB,UAAW,EAEvE,0BAAyB,0BAAY,SAA0C,CACnF,GAAI,CAAC,mBACH,MAAM,IAAI,MAAM,8DAA8D,EAIhF,IAAM,kCAAoC,CACxC,aAAc,sBACd,KAAM,CAAE,GAAG,kBAAmB,EAC9B,yBAA0B,CACxB,CACE,OAAQ,CACN,CACE,OAAQ,CACN,CACE,mBAAoB,YAAc,EACpC,CACF,EACA,KAAM,CACJ,OAAQ,CACN,CACE,OAAQ,2CACV,CACF,CACF,CACF,CACF,CACF,CACF,CACF,EAEA,OAAO,QAAQ,WAAW,qCAAsC,iCAAiC,CACnG,EAAG,CAAC,mBAAoB,WAAY,OAAO,CAAC,EAEtC,qBAAoB,0BACxB,MAAO,YACG,MAAM,QAAQ,WAAW,gCAAiC,CAAE,KAAM,UAAW,CAAC,EAExF,CAAC,OAAO,CACV,EAwBA,MAAO,CACL,MACA,kBACA,sBArB6B,YAA2D,CACxF,IAAI,mBACA,gCAAkB,UAAU,EAC9B,gBAAkB,CAAE,GAAG,UAAW,KACzB,uBAAS,UAAU,IAC5B,gBAAkB,CAChB,KAAM,WAAW,SAAW,GAC5B,OAAQ,CAAC,UAAU,CACrB,GAEF,6BAA6B,eAAe,CAC9C,EAWE,gCA1BuCC,aAAyC,CAChF,qBAAqBA,WAAU,CACjC,EAyBE,uBACA,MAXY,IAAY,CACxB,6BAA6B,MAAS,EACtC,qBAAqB,MAAS,CAChC,CASA,CACF,CC/GA,IAAAC,oBAA2B,gCAC3BC,cAA4C,iBA0BrC,SAAS,kBAAkB,QAAoD,CACpF,IAAM,WAAU,gCAAW,EACrB,CAAE,UAAW,WAAY,qBAAsB,gBAAiB,oBAAqB,OAAQ,EAAI,QACjG,CAAC,UAAW,YAAY,KAAI,wBAA6B,MAAS,EAElE,2BAA0B,sBAAO,oBAAoB,EACrD,sBAAqB,sBAAO,eAAe,EAC3C,0BAAyB,sBAAO,mBAAmB,EACnD,cAAa,sBAAO,OAAO,EAEjC,kCAAU,IAAM,CACd,wBAAwB,QAAU,qBAClC,mBAAmB,QAAU,gBAC7B,uBAAuB,QAAU,oBACjC,WAAW,QAAU,OACvB,EAAG,CAAC,qBAAsB,gBAAiB,oBAAqB,OAAO,CAAC,KAExE,yBAAU,IAAM,CACd,IAAI,UAAY,GA+BhB,OA7BY,SAA2B,CACrC,GAAI,YAAc,CAAC,sBAAsB,QAAQ,qBAAqB,CAAC,EAAG,CACxE,IAAM,aAAgB,MAAM,QAAQ,WAClC,oCACA,CAAC,CACH,EACA,GAAI,UACF,OAEF,uBAAuB,UAAU,YAAY,CAC/C,CAEA,GAAI,UAAW,CAEb,GADA,MAAM,QAAQ,WAAW,0BAA2B,CAAE,SAAU,CAAC,EAC7D,UACF,OAEF,wBAAwB,UAAU,CACpC,CACA,IAAM,OAAS,MAAM,QAAQ,WAAW,oBAAqB,CAAE,SAAU,CAAC,EACtE,WAGA,OAAO,MACT,aAAa,OAAO,GAAG,EACvB,mBAAmB,UAAU,OAAO,GAAG,EAE3C,GAEI,EAAE,MAAO,KAAiB,CACvB,WACH,WAAW,UAAU,GAAG,CAE5B,CAAC,EAEM,IAAY,CACjB,UAAY,EACd,CACF,EAAG,CAAC,QAAS,UAAW,UAAU,CAAC,EAE5B,SACT,CAQA,SAAS,sBAAsB,WAAoD,CACjF,MAAO,CAAC,CAAC,YAAY,YAAY,KAAM,GAAM,EAAE,QAAQ,SAAS,UAAU,CAAC,CAC7E,CCpGA,IAAAC,oBAA2B,gCAC3BC,cAAyD,iBAUzD,IAAM,sCAAwC,IAEvC,SAAS,yBAAyB,QAA4D,CACnG,IAAM,WAAU,gCAAW,EACrB,CAAE,SAAU,OAAQ,EAAI,SAAW,CAAC,EACpC,YAAc,QAAQ,qBAAqB,GAAG,YAAY,KAAM,GAAM,EAAE,QAAQ,SAAS,UAAU,CAAC,EACpG,gBAAkB,SAAS,6BAA+B,sCAC1D,YAAW,sBAAmC,MAAS,EACvD,CAAC,YAAa,cAAc,KAAI,wBAA6B,MAAS,EAEtE,aAAY,2BAAY,IAAM,CAClC,IAAM,QAAU,SAAS,QACrB,SACF,cAAc,OAAO,CAEzB,EAAG,CAAC,CAAC,EAEC,eAAc,2BAAY,SAAY,CAC1C,GAAI,CACF,IAAM,OAAU,MAAM,QAAQ,WAC5B,iCACA,CAAC,CACH,EAEI,SAAW,EACX,OAAO,4BACT,UAAY,OAAO,2BAEjB,OAAO,uBACT,UAAY,OAAO,sBAEjB,OAAO,sBACT,UAAY,OAAO,qBAEjB,OAAO,yBACT,UAAY,OAAO,wBAEjB,WAAa,cACf,eAAe,QAAQ,EACvB,WAAW,QAAQ,EAEvB,OAAS,IAAc,CACrB,UAAU,GAAG,EACb,UAAU,CACZ,CACF,EAAG,CAAC,QAAS,YAAa,SAAU,QAAS,SAAS,CAAC,EAEjD,cAAa,2BAAY,IAAM,CACnC,SAAS,QAAU,YAAY,IAAM,CACnC,YAAY,EAAE,MAAM,QAAQ,KAAK,CACnC,EAAG,eAAe,CACpB,EAAG,CAAC,YAAa,eAAe,CAAC,EAEjC,kCAAU,KAEJ,aACF,WAAW,EAIN,WACN,CAAC,YAAa,WAAY,SAAS,CAAC,EAEhC,WACT,CC1EA,IAAAC,oBAA2B,gCAC3BC,cAAsC,iBCetC,IAAAC,aAWO,yBAzBM,kBAAqB,YACzB,YAAY,MAAM,MAAQ,GAQ5B,SAAS,8BAA8B,aAAgD,CAC5F,IAAM,GAAK,aAAa,YAAY,KAAM,GAAM,EAAE,SAAW,2BAA2B,GAAG,MAC3F,OAAO,GAAK,OAAO,SAAS,GAAI,EAAE,EAAI,MACxC,CDmGO,SAAS,4BAA4D,CAC1E,GAAM,CAAC,iBAAkB,0BAA0B,KAAI,wBAAmC,MAAS,EAC7F,CAAC,kBAAmB,mBAAmB,KAAI,wBAAS,EAAK,EACzD,WAAU,gCAAW,EAErB,MAAsC,CAC1C,iBACA,aAAc,iBAChB,EAEM,oBAAmB,2BACvB,MAAO,QACG,MAAM,QAAQ,WAAW,6BAA8B,MAAM,EAEvE,CAAC,OAAO,CACV,EAEM,oBAAuB,UAA6C,CACxE,2BAA2B,QAAQ,CACrC,EAEM,aAAgB,SAA2B,CAC/C,oBAAoB,OAAO,CAC7B,EAEM,uBAAsB,2BAC1B,MAAO,WAA2D,CAChE,GAAI,CAAC,iBACH,MAAM,IAAI,MAAM,uDAAuD,EAIzE,GAAI,CADe,8BAA8B,gBAAgB,EAE/D,MAAM,IAAI,MAAM,8DAA8D,EAGhF,OAAO,QAAQ,WAAW,kCAAmC,CAC3D,UACA,SAAU,iBACV,aAAc,iBAChB,CAAC,CACH,EACA,CAAC,iBAAkB,kBAAmB,OAAO,CAC/C,EAOA,MAAO,CACL,MACA,iBACA,oBACA,aACA,oBACA,MAXY,IAAY,CACxB,2BAA2B,MAAS,EACpC,oBAAoB,EAAK,CAC3B,CASA,CACF,CE1KA,IAAAC,oBAAkC,gCAa3B,SAAS,2BAA6D,CAC3E,SAAO,uCAAkB,6BAA8B,iCAAiC,CAC1F,CCjBA,IAAAC,oBAA2B,gCAC3BC,cAAyD,iBAsClD,SAAS,0BAA0B,QAIxC,CACA,IAAM,WAAU,gCAAW,EACrB,QAAU,SAAS,SAAW,GAC9B,mBAAkB,sBAAO,EAAK,EAC9B,CAAC,OAAQ,SAAS,KAAI,wBAAmD,MAAS,EAClF,CAAC,QAAS,UAAU,KAAI,wBAAS,OAAO,EACxC,CAAC,MAAO,QAAQ,KAAI,wBAAkB,MAAS,EAE/C,gBAAe,sBAAO,SAAS,SAAS,EAC9C,aAAa,QAAU,SAAS,UAEhC,IAAM,cAAa,sBAAO,SAAS,OAAO,EAC1C,WAAW,QAAU,SAAS,QAE9B,IAAM,UAAS,2BAAY,SAAY,CACrC,GAAI,kBAAgB,SAAW,CAAC,SAIhC,iBAAgB,QAAU,GAC1B,WAAW,EAAI,EACf,SAAS,MAAS,EAElB,GAAI,CACF,IAAM,aAAgB,MAAM,QAAQ,WAClC,oCACA,CAAC,CACH,EAEA,UAAU,YAAY,EACtB,aAAa,UAAU,YAAY,CACrC,OAAS,IAAc,CACrB,SAAS,GAAG,EACZ,WAAW,UAAU,GAAG,CAC1B,QAAE,CACA,WAAW,EAAK,CAClB,EACF,EAAG,CAAC,QAAS,OAAO,CAAC,EAErB,kCAAU,IAAM,CACd,OAAO,EAAE,MAAM,QAAQ,KAAK,CAC9B,EAAG,CAAC,MAAM,CAAC,EAEJ,CAAE,OAAQ,QAAS,KAAM,CAClC",
|
|
6
|
+
"names": ["import_core", "directions", "import_react_hooks", "import_react", "import_react_hooks", "import_react", "import_react_hooks", "import_react", "import_core", "import_react_hooks", "import_react_hooks", "import_react"]
|
|
7
7
|
}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { DOSESPOT_PHARMACY_ID_SYSTEM } from '@medplum/dosespot-core';
|
|
|
16
16
|
import { DOSESPOT_PRESCRIPTIONS_SYNC_BOT } from '@medplum/dosespot-core';
|
|
17
17
|
import { DOSESPOT_SEARCH_MEDICATIONS_BOT } from '@medplum/dosespot-core';
|
|
18
18
|
import { DOSESPOT_SEARCH_PHARMACY_BOT } from '@medplum/dosespot-core';
|
|
19
|
+
import { DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT } from '@medplum/dosespot-core';
|
|
19
20
|
import { DoseSpotNotificationCountsResponse } from '@medplum/dosespot-core';
|
|
20
21
|
import type { EPrescribingIFrameOptions } from '@medplum/react-hooks';
|
|
21
22
|
import { getPreferredPharmaciesFromPatient } from '@medplum/core';
|
|
@@ -76,6 +77,8 @@ export { DOSESPOT_SEARCH_MEDICATIONS_BOT }
|
|
|
76
77
|
|
|
77
78
|
export { DOSESPOT_SEARCH_PHARMACY_BOT }
|
|
78
79
|
|
|
80
|
+
export { DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT }
|
|
81
|
+
|
|
79
82
|
export declare interface DoseSpotClinicFormularyReturn {
|
|
80
83
|
state: DoseSpotClinicFormularyState;
|
|
81
84
|
/**
|
|
@@ -105,7 +108,17 @@ export declare interface DoseSpotClinicFormularyState {
|
|
|
105
108
|
directions: string | undefined;
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
export declare
|
|
111
|
+
export declare interface DoseSpotIFrameOptions extends EPrescribingIFrameOptions {
|
|
112
|
+
/**
|
|
113
|
+
* When true, automatically runs the self-enrollment bot before loading
|
|
114
|
+
* the iframe if the current user does not have a DoseSpot identifier
|
|
115
|
+
* on their ProjectMembership. Requires an active PractitionerRole with
|
|
116
|
+
* DoseSpot role type codes for the practitioner.
|
|
117
|
+
*/
|
|
118
|
+
readonly selfEnroll?: boolean;
|
|
119
|
+
/** Called after self-enrollment completes successfully. */
|
|
120
|
+
readonly onSelfEnrollSuccess?: (result: DoseSpotSelfEnrollmentResult) => void;
|
|
121
|
+
}
|
|
109
122
|
|
|
110
123
|
export { DoseSpotNotificationCountsResponse }
|
|
111
124
|
|
|
@@ -155,6 +168,29 @@ export declare interface DoseSpotPatientPharmacyState {
|
|
|
155
168
|
setAsPrimary: boolean;
|
|
156
169
|
}
|
|
157
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Options for the {@link useDoseSpotSelfEnrollment} hook.
|
|
173
|
+
*/
|
|
174
|
+
export declare interface DoseSpotSelfEnrollmentOptions {
|
|
175
|
+
/** Whether to run the self-enrollment bot. Defaults to true. */
|
|
176
|
+
readonly enabled?: boolean;
|
|
177
|
+
/** Called when enrollment succeeds. */
|
|
178
|
+
readonly onSuccess?: (result: DoseSpotSelfEnrollmentResult) => void;
|
|
179
|
+
/** Called when enrollment fails. */
|
|
180
|
+
readonly onError?: (err: unknown) => void;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Result returned by the DoseSpot self-enrollment bot.
|
|
185
|
+
*/
|
|
186
|
+
export declare interface DoseSpotSelfEnrollmentResult {
|
|
187
|
+
status: 'created' | 'already_enrolled' | 'advanced';
|
|
188
|
+
doseSpotClinicianId: number;
|
|
189
|
+
registrationStatus: string;
|
|
190
|
+
epcsEnabled: boolean;
|
|
191
|
+
nextSteps: string[];
|
|
192
|
+
}
|
|
193
|
+
|
|
158
194
|
export declare const getMedicationName: (medication: MedicationKnowledge | undefined) => string;
|
|
159
195
|
|
|
160
196
|
/**
|
|
@@ -214,8 +250,9 @@ export declare function useDoseSpotClinicFormulary(): DoseSpotClinicFormularyRet
|
|
|
214
250
|
/**
|
|
215
251
|
* React hook that syncs a patient to DoseSpot and returns the iframe URL.
|
|
216
252
|
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
253
|
+
* Runs optional self-enrollment, then the patient-sync bot (when `patientId`
|
|
254
|
+
* is set), then the iframe bot — aligned with {@link useEPrescribingIFrame}
|
|
255
|
+
* behavior plus DoseSpot-specific enrollment.
|
|
219
256
|
*
|
|
220
257
|
* @param options - Configuration and callback options.
|
|
221
258
|
* @returns The DoseSpot iframe URL, or undefined while loading.
|
|
@@ -269,4 +306,22 @@ export declare function useDoseSpotPharmacySearch(): UseDoseSpotPharmacySearchRe
|
|
|
269
306
|
|
|
270
307
|
export declare type UseDoseSpotPharmacySearchReturn = UsePharmacySearchReturn;
|
|
271
308
|
|
|
309
|
+
/**
|
|
310
|
+
* React hook that executes the DoseSpot self-enrollment bot.
|
|
311
|
+
*
|
|
312
|
+
* Runs the `dosespot-self-enroll-prescriber-bot` once on mount (when enabled)
|
|
313
|
+
* and returns the enrollment result, loading state, and any error.
|
|
314
|
+
*
|
|
315
|
+
* The bot is idempotent -- it creates the clinician on the first call and
|
|
316
|
+
* auto-advances through registration stages (IDP, TFA) on subsequent calls.
|
|
317
|
+
*
|
|
318
|
+
* @param options - Configuration options.
|
|
319
|
+
* @returns An object with `result`, `loading`, and `error` fields.
|
|
320
|
+
*/
|
|
321
|
+
export declare function useDoseSpotSelfEnrollment(options?: DoseSpotSelfEnrollmentOptions): {
|
|
322
|
+
result: DoseSpotSelfEnrollmentResult | undefined;
|
|
323
|
+
loading: boolean;
|
|
324
|
+
error: unknown;
|
|
325
|
+
};
|
|
326
|
+
|
|
272
327
|
export { }
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { DOSESPOT_PHARMACY_ID_SYSTEM } from '@medplum/dosespot-core';
|
|
|
16
16
|
import { DOSESPOT_PRESCRIPTIONS_SYNC_BOT } from '@medplum/dosespot-core';
|
|
17
17
|
import { DOSESPOT_SEARCH_MEDICATIONS_BOT } from '@medplum/dosespot-core';
|
|
18
18
|
import { DOSESPOT_SEARCH_PHARMACY_BOT } from '@medplum/dosespot-core';
|
|
19
|
+
import { DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT } from '@medplum/dosespot-core';
|
|
19
20
|
import { DoseSpotNotificationCountsResponse } from '@medplum/dosespot-core';
|
|
20
21
|
import type { EPrescribingIFrameOptions } from '@medplum/react-hooks';
|
|
21
22
|
import { getPreferredPharmaciesFromPatient } from '@medplum/core';
|
|
@@ -76,6 +77,8 @@ export { DOSESPOT_SEARCH_MEDICATIONS_BOT }
|
|
|
76
77
|
|
|
77
78
|
export { DOSESPOT_SEARCH_PHARMACY_BOT }
|
|
78
79
|
|
|
80
|
+
export { DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT }
|
|
81
|
+
|
|
79
82
|
export declare interface DoseSpotClinicFormularyReturn {
|
|
80
83
|
state: DoseSpotClinicFormularyState;
|
|
81
84
|
/**
|
|
@@ -105,7 +108,17 @@ export declare interface DoseSpotClinicFormularyState {
|
|
|
105
108
|
directions: string | undefined;
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
export declare
|
|
111
|
+
export declare interface DoseSpotIFrameOptions extends EPrescribingIFrameOptions {
|
|
112
|
+
/**
|
|
113
|
+
* When true, automatically runs the self-enrollment bot before loading
|
|
114
|
+
* the iframe if the current user does not have a DoseSpot identifier
|
|
115
|
+
* on their ProjectMembership. Requires an active PractitionerRole with
|
|
116
|
+
* DoseSpot role type codes for the practitioner.
|
|
117
|
+
*/
|
|
118
|
+
readonly selfEnroll?: boolean;
|
|
119
|
+
/** Called after self-enrollment completes successfully. */
|
|
120
|
+
readonly onSelfEnrollSuccess?: (result: DoseSpotSelfEnrollmentResult) => void;
|
|
121
|
+
}
|
|
109
122
|
|
|
110
123
|
export { DoseSpotNotificationCountsResponse }
|
|
111
124
|
|
|
@@ -155,6 +168,29 @@ export declare interface DoseSpotPatientPharmacyState {
|
|
|
155
168
|
setAsPrimary: boolean;
|
|
156
169
|
}
|
|
157
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Options for the {@link useDoseSpotSelfEnrollment} hook.
|
|
173
|
+
*/
|
|
174
|
+
export declare interface DoseSpotSelfEnrollmentOptions {
|
|
175
|
+
/** Whether to run the self-enrollment bot. Defaults to true. */
|
|
176
|
+
readonly enabled?: boolean;
|
|
177
|
+
/** Called when enrollment succeeds. */
|
|
178
|
+
readonly onSuccess?: (result: DoseSpotSelfEnrollmentResult) => void;
|
|
179
|
+
/** Called when enrollment fails. */
|
|
180
|
+
readonly onError?: (err: unknown) => void;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Result returned by the DoseSpot self-enrollment bot.
|
|
185
|
+
*/
|
|
186
|
+
export declare interface DoseSpotSelfEnrollmentResult {
|
|
187
|
+
status: 'created' | 'already_enrolled' | 'advanced';
|
|
188
|
+
doseSpotClinicianId: number;
|
|
189
|
+
registrationStatus: string;
|
|
190
|
+
epcsEnabled: boolean;
|
|
191
|
+
nextSteps: string[];
|
|
192
|
+
}
|
|
193
|
+
|
|
158
194
|
export declare const getMedicationName: (medication: MedicationKnowledge | undefined) => string;
|
|
159
195
|
|
|
160
196
|
/**
|
|
@@ -214,8 +250,9 @@ export declare function useDoseSpotClinicFormulary(): DoseSpotClinicFormularyRet
|
|
|
214
250
|
/**
|
|
215
251
|
* React hook that syncs a patient to DoseSpot and returns the iframe URL.
|
|
216
252
|
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
253
|
+
* Runs optional self-enrollment, then the patient-sync bot (when `patientId`
|
|
254
|
+
* is set), then the iframe bot — aligned with {@link useEPrescribingIFrame}
|
|
255
|
+
* behavior plus DoseSpot-specific enrollment.
|
|
219
256
|
*
|
|
220
257
|
* @param options - Configuration and callback options.
|
|
221
258
|
* @returns The DoseSpot iframe URL, or undefined while loading.
|
|
@@ -269,4 +306,22 @@ export declare function useDoseSpotPharmacySearch(): UseDoseSpotPharmacySearchRe
|
|
|
269
306
|
|
|
270
307
|
export declare type UseDoseSpotPharmacySearchReturn = UsePharmacySearchReturn;
|
|
271
308
|
|
|
309
|
+
/**
|
|
310
|
+
* React hook that executes the DoseSpot self-enrollment bot.
|
|
311
|
+
*
|
|
312
|
+
* Runs the `dosespot-self-enroll-prescriber-bot` once on mount (when enabled)
|
|
313
|
+
* and returns the enrollment result, loading state, and any error.
|
|
314
|
+
*
|
|
315
|
+
* The bot is idempotent -- it creates the clinician on the first call and
|
|
316
|
+
* auto-advances through registration stages (IDP, TFA) on subsequent calls.
|
|
317
|
+
*
|
|
318
|
+
* @param options - Configuration options.
|
|
319
|
+
* @returns An object with `result`, `loading`, and `error` fields.
|
|
320
|
+
*/
|
|
321
|
+
export declare function useDoseSpotSelfEnrollment(options?: DoseSpotSelfEnrollmentOptions): {
|
|
322
|
+
result: DoseSpotSelfEnrollmentResult | undefined;
|
|
323
|
+
loading: boolean;
|
|
324
|
+
error: unknown;
|
|
325
|
+
};
|
|
326
|
+
|
|
272
327
|
export { }
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{addPreferredPharmacyToPatient,createPreferredPharmacyExtension,getPreferredPharmaciesFromPatient,isAddPharmacyResponse,isOrganizationArray,PATIENT_PREFERRED_PHARMACY_URL,PHARMACY_PREFERENCE_TYPE_SYSTEM,PHARMACY_TYPE_PREFERRED,PHARMACY_TYPE_PRIMARY,removePreferredPharmacyFromPatient}from"@medplum/core";var MEDPLUM_BOT_SYSTEM="https://www.medplum.com/bots",DOSESPOT_PATIENT_ID_SYSTEM="https://dosespot.com/patient-id",DOSESPOT_PHARMACY_ID_SYSTEM="https://dosespot.com/pharmacy-id",DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM="https://dosespot.com/clinic-favorite-medication-id",DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM="https://dosespot.com/dispensable-drug-id",DOSESPOT_SEARCH_PHARMACY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-search-pharmacy-bot"},DOSESPOT_ADD_PATIENT_PHARMACY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-add-patient-pharmacy-bot"},DOSESPOT_PATIENT_SYNC_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-patient-sync-bot"},DOSESPOT_IFRAME_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-iframe-bot"},DOSESPOT_ADD_FAVORITE_MEDICATION_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-add-favorite-medication-bot"},DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-get-favorite-medications-bot"},DOSESPOT_SEARCH_MEDICATIONS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-search-medication-bot"},DOSESPOT_MEDICATION_HISTORY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-medication-history-bot"},DOSESPOT_PRESCRIPTIONS_SYNC_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-prescriptions-sync-bot"},DOSESPOT_NOTIFICATION_COUNTS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-notification-counts-bot"};import{isCodeableConcept,isCoding}from"@medplum/core";import{useMedplum}from"@medplum/react-hooks";import{useCallback,useState}from"react";function useDoseSpotClinicFormulary(){let[directions,privateSetDirections]=useState(void 0),[selectedMedication,privateSetSelectedMedication]=useState(void 0),medplum=useMedplum(),state={selectedMedication,directions},saveFavoriteMedication=useCallback(async()=>{if(!selectedMedication)throw new Error("Must select a medication before adding a favorite medication");let medicationKnowledgeWithDirections={resourceType:"MedicationKnowledge",code:{...selectedMedication},administrationGuidelines:[{dosage:[{dosage:[{patientInstruction:directions||""}],type:{coding:[{system:"https://dosespot.com/patient-instructions"}]}}]}]};return medplum.executeBot(DOSESPOT_ADD_FAVORITE_MEDICATION_BOT,medicationKnowledgeWithDirections)},[selectedMedication,directions,medplum]),searchMedications=useCallback(async searchTerm=>await medplum.executeBot(DOSESPOT_SEARCH_MEDICATIONS_BOT,{name:searchTerm}),[medplum]);return{state,searchMedications,setSelectedMedication:medication=>{let medicationToSet;isCodeableConcept(medication)?medicationToSet={...medication}:isCoding(medication)&&(medicationToSet={text:medication.display||"",coding:[medication]}),privateSetSelectedMedication(medicationToSet)},setSelectedMedicationDirections:directions2=>{privateSetDirections(directions2)},saveFavoriteMedication,clear:()=>{privateSetSelectedMedication(void 0),privateSetDirections(void 0)}}}import{useEPrescribingIFrame}from"@medplum/react-hooks";function useDoseSpotIFrame(options){return useEPrescribingIFrame(DOSESPOT_PATIENT_SYNC_BOT,DOSESPOT_IFRAME_BOT,options)}import{useMedplum as useMedplum2}from"@medplum/react-hooks";import{useCallback as useCallback2,useEffect,useRef,useState as useState2}from"react";var DEFAULT_REFRESH_INTERVAL_MILLISECONDS=1e4;function useDoseSpotNotifications(options){let medplum=useMedplum2(),{onChange,onError}=options??{},hasDoseSpot=medplum.getProjectMembership()?.identifier?.some(i=>i.system?.includes("dosespot")),refreshInterval=options?.refreshIntervalMilliseconds??DEFAULT_REFRESH_INTERVAL_MILLISECONDS,timerRef=useRef(void 0),[unreadCount,setUnreadCount]=useState2(void 0),stopTimer=useCallback2(()=>{let timerId=timerRef.current;timerId&&clearInterval(timerId)},[]),updateCount=useCallback2(async()=>{try{let result=await medplum.executeBot(DOSESPOT_NOTIFICATION_COUNTS_BOT,{}),newCount=0;result.PendingPrescriptionsCount&&(newCount+=result.PendingPrescriptionsCount),result.PendingRxChangeCount&&(newCount+=result.PendingRxChangeCount),result.RefillRequestsCount&&(newCount+=result.RefillRequestsCount),result.TransactionErrorsCount&&(newCount+=result.TransactionErrorsCount),newCount!==unreadCount&&(setUnreadCount(newCount),onChange?.(newCount))}catch(err){onError?.(err),stopTimer()}},[medplum,unreadCount,onChange,onError,stopTimer]),startTimer=useCallback2(()=>{timerRef.current=setInterval(()=>{updateCount().catch(console.error)},refreshInterval)},[updateCount,refreshInterval]);return useEffect(()=>(hasDoseSpot&&startTimer(),stopTimer),[hasDoseSpot,startTimer,stopTimer]),unreadCount}import{useMedplum as useMedplum3}from"@medplum/react-hooks";import{useCallback as useCallback3,useState as useState3}from"react";import{addPreferredPharmacyToPatient as addPreferredPharmacyToPatient2,createPreferredPharmacyExtension as createPreferredPharmacyExtension2,getPreferredPharmaciesFromPatient as getPreferredPharmaciesFromPatient2,isAddPharmacyResponse as isAddPharmacyResponse2,isOrganizationArray as isOrganizationArray2,PATIENT_PREFERRED_PHARMACY_URL as PATIENT_PREFERRED_PHARMACY_URL2,PHARMACY_PREFERENCE_TYPE_SYSTEM as PHARMACY_PREFERENCE_TYPE_SYSTEM2,PHARMACY_TYPE_PREFERRED as PHARMACY_TYPE_PREFERRED2,PHARMACY_TYPE_PRIMARY as PHARMACY_TYPE_PRIMARY2,removePreferredPharmacyFromPatient as removePreferredPharmacyFromPatient2}from"@medplum/core";var getMedicationName=medication=>medication?.code?.text||"";function getPharmacyIdFromOrganization(organization){let id=organization.identifier?.find(i=>i.system===DOSESPOT_PHARMACY_ID_SYSTEM)?.value;return id?Number.parseInt(id,10):void 0}function useDoseSpotPatientPharmacy(){let[selectedPharmacy,privateSetSelectedPharmacy]=useState3(void 0),[setAsPrimaryState,privateSetAsPrimary]=useState3(!1),medplum=useMedplum3(),state={selectedPharmacy,setAsPrimary:setAsPrimaryState},searchPharmacies=useCallback3(async params=>await medplum.executeBot(DOSESPOT_SEARCH_PHARMACY_BOT,params),[medplum]),setSelectedPharmacy=pharmacy=>{privateSetSelectedPharmacy(pharmacy)},setAsPrimary=primary=>{privateSetAsPrimary(primary)},addFavoritePharmacy=useCallback3(async patientId=>{if(!selectedPharmacy)throw new Error("Must select a pharmacy before adding it as a favorite");if(!getPharmacyIdFromOrganization(selectedPharmacy))throw new Error("Selected pharmacy does not have a valid DoseSpot pharmacy ID");return medplum.executeBot(DOSESPOT_ADD_PATIENT_PHARMACY_BOT,{patientId,pharmacy:selectedPharmacy,setAsPrimary:setAsPrimaryState})},[selectedPharmacy,setAsPrimaryState,medplum]);return{state,searchPharmacies,setSelectedPharmacy,setAsPrimary,addFavoritePharmacy,clear:()=>{privateSetSelectedPharmacy(void 0),privateSetAsPrimary(!1)}}}import{usePharmacySearch}from"@medplum/react-hooks";function useDoseSpotPharmacySearch(){return usePharmacySearch(DOSESPOT_SEARCH_PHARMACY_BOT,DOSESPOT_ADD_PATIENT_PHARMACY_BOT)}export{DOSESPOT_ADD_FAVORITE_MEDICATION_BOT,DOSESPOT_ADD_PATIENT_PHARMACY_BOT,DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM,DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM,DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT,DOSESPOT_IFRAME_BOT,DOSESPOT_MEDICATION_HISTORY_BOT,DOSESPOT_NOTIFICATION_COUNTS_BOT,DOSESPOT_PATIENT_ID_SYSTEM,DOSESPOT_PATIENT_SYNC_BOT,DOSESPOT_PHARMACY_ID_SYSTEM,DOSESPOT_PRESCRIPTIONS_SYNC_BOT,DOSESPOT_SEARCH_MEDICATIONS_BOT,DOSESPOT_SEARCH_PHARMACY_BOT,MEDPLUM_BOT_SYSTEM,PATIENT_PREFERRED_PHARMACY_URL2 as PATIENT_PREFERRED_PHARMACY_URL,PHARMACY_PREFERENCE_TYPE_SYSTEM2 as PHARMACY_PREFERENCE_TYPE_SYSTEM,PHARMACY_TYPE_PREFERRED2 as PHARMACY_TYPE_PREFERRED,PHARMACY_TYPE_PRIMARY2 as PHARMACY_TYPE_PRIMARY,addPreferredPharmacyToPatient2 as addPreferredPharmacyToPatient,createPreferredPharmacyExtension2 as createPreferredPharmacyExtension,getMedicationName,getPharmacyIdFromOrganization,getPreferredPharmaciesFromPatient2 as getPreferredPharmaciesFromPatient,isAddPharmacyResponse2 as isAddPharmacyResponse,isOrganizationArray2 as isOrganizationArray,removePreferredPharmacyFromPatient2 as removePreferredPharmacyFromPatient,useDoseSpotClinicFormulary,useDoseSpotIFrame,useDoseSpotNotifications,useDoseSpotPatientPharmacy,useDoseSpotPharmacySearch};
|
|
1
|
+
import{addPreferredPharmacyToPatient,createPreferredPharmacyExtension,getPreferredPharmaciesFromPatient,isAddPharmacyResponse,isOrganizationArray,PATIENT_PREFERRED_PHARMACY_URL,PHARMACY_PREFERENCE_TYPE_SYSTEM,PHARMACY_TYPE_PREFERRED,PHARMACY_TYPE_PRIMARY,removePreferredPharmacyFromPatient}from"@medplum/core";var MEDPLUM_BOT_SYSTEM="https://www.medplum.com/bots",DOSESPOT_PATIENT_ID_SYSTEM="https://dosespot.com/patient-id",DOSESPOT_PHARMACY_ID_SYSTEM="https://dosespot.com/pharmacy-id",DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM="https://dosespot.com/clinic-favorite-medication-id",DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM="https://dosespot.com/dispensable-drug-id",DOSESPOT_SEARCH_PHARMACY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-search-pharmacy-bot"},DOSESPOT_ADD_PATIENT_PHARMACY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-add-patient-pharmacy-bot"},DOSESPOT_PATIENT_SYNC_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-patient-sync-bot"},DOSESPOT_IFRAME_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-iframe-bot"},DOSESPOT_ADD_FAVORITE_MEDICATION_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-add-favorite-medication-bot"},DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-get-favorite-medications-bot"},DOSESPOT_SEARCH_MEDICATIONS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-search-medication-bot"},DOSESPOT_MEDICATION_HISTORY_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-medication-history-bot"},DOSESPOT_PRESCRIPTIONS_SYNC_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-prescriptions-sync-bot"},DOSESPOT_NOTIFICATION_COUNTS_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-notification-counts-bot"},DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT={system:MEDPLUM_BOT_SYSTEM,value:"dosespot-self-enroll-prescriber-bot"};import{isCodeableConcept,isCoding}from"@medplum/core";import{useMedplum}from"@medplum/react-hooks";import{useCallback,useState}from"react";function useDoseSpotClinicFormulary(){let[directions,privateSetDirections]=useState(void 0),[selectedMedication,privateSetSelectedMedication]=useState(void 0),medplum=useMedplum(),state={selectedMedication,directions},saveFavoriteMedication=useCallback(async()=>{if(!selectedMedication)throw new Error("Must select a medication before adding a favorite medication");let medicationKnowledgeWithDirections={resourceType:"MedicationKnowledge",code:{...selectedMedication},administrationGuidelines:[{dosage:[{dosage:[{patientInstruction:directions||""}],type:{coding:[{system:"https://dosespot.com/patient-instructions"}]}}]}]};return medplum.executeBot(DOSESPOT_ADD_FAVORITE_MEDICATION_BOT,medicationKnowledgeWithDirections)},[selectedMedication,directions,medplum]),searchMedications=useCallback(async searchTerm=>await medplum.executeBot(DOSESPOT_SEARCH_MEDICATIONS_BOT,{name:searchTerm}),[medplum]);return{state,searchMedications,setSelectedMedication:medication=>{let medicationToSet;isCodeableConcept(medication)?medicationToSet={...medication}:isCoding(medication)&&(medicationToSet={text:medication.display||"",coding:[medication]}),privateSetSelectedMedication(medicationToSet)},setSelectedMedicationDirections:directions2=>{privateSetDirections(directions2)},saveFavoriteMedication,clear:()=>{privateSetSelectedMedication(void 0),privateSetDirections(void 0)}}}import{useMedplum as useMedplum2}from"@medplum/react-hooks";import{useEffect,useRef,useState as useState2}from"react";function useDoseSpotIFrame(options){let medplum=useMedplum2(),{patientId,selfEnroll,onPatientSyncSuccess,onIframeSuccess,onSelfEnrollSuccess,onError}=options,[iframeUrl,setIframeUrl]=useState2(void 0),onPatientSyncSuccessRef=useRef(onPatientSyncSuccess),onIframeSuccessRef=useRef(onIframeSuccess),onSelfEnrollSuccessRef=useRef(onSelfEnrollSuccess),onErrorRef=useRef(onError);return useEffect(()=>{onPatientSyncSuccessRef.current=onPatientSyncSuccess,onIframeSuccessRef.current=onIframeSuccess,onSelfEnrollSuccessRef.current=onSelfEnrollSuccess,onErrorRef.current=onError},[onPatientSyncSuccess,onIframeSuccess,onSelfEnrollSuccess,onError]),useEffect(()=>{let cancelled=!1;return(async()=>{if(selfEnroll&&!hasDoseSpotIdentifier(medplum.getProjectMembership())){let enrollResult=await medplum.executeBot(DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,{});if(cancelled)return;onSelfEnrollSuccessRef.current?.(enrollResult)}if(patientId){if(await medplum.executeBot(DOSESPOT_PATIENT_SYNC_BOT,{patientId}),cancelled)return;onPatientSyncSuccessRef.current?.()}let result=await medplum.executeBot(DOSESPOT_IFRAME_BOT,{patientId});cancelled||result.url&&(setIframeUrl(result.url),onIframeSuccessRef.current?.(result.url))})().catch(err=>{cancelled||onErrorRef.current?.(err)}),()=>{cancelled=!0}},[medplum,patientId,selfEnroll]),iframeUrl}function hasDoseSpotIdentifier(membership){return!!membership?.identifier?.some(i=>i.system?.includes("dosespot"))}import{useMedplum as useMedplum3}from"@medplum/react-hooks";import{useCallback as useCallback2,useEffect as useEffect2,useRef as useRef2,useState as useState3}from"react";var DEFAULT_REFRESH_INTERVAL_MILLISECONDS=1e4;function useDoseSpotNotifications(options){let medplum=useMedplum3(),{onChange,onError}=options??{},hasDoseSpot=medplum.getProjectMembership()?.identifier?.some(i=>i.system?.includes("dosespot")),refreshInterval=options?.refreshIntervalMilliseconds??DEFAULT_REFRESH_INTERVAL_MILLISECONDS,timerRef=useRef2(void 0),[unreadCount,setUnreadCount]=useState3(void 0),stopTimer=useCallback2(()=>{let timerId=timerRef.current;timerId&&clearInterval(timerId)},[]),updateCount=useCallback2(async()=>{try{let result=await medplum.executeBot(DOSESPOT_NOTIFICATION_COUNTS_BOT,{}),newCount=0;result.PendingPrescriptionsCount&&(newCount+=result.PendingPrescriptionsCount),result.PendingRxChangeCount&&(newCount+=result.PendingRxChangeCount),result.RefillRequestsCount&&(newCount+=result.RefillRequestsCount),result.TransactionErrorsCount&&(newCount+=result.TransactionErrorsCount),newCount!==unreadCount&&(setUnreadCount(newCount),onChange?.(newCount))}catch(err){onError?.(err),stopTimer()}},[medplum,unreadCount,onChange,onError,stopTimer]),startTimer=useCallback2(()=>{timerRef.current=setInterval(()=>{updateCount().catch(console.error)},refreshInterval)},[updateCount,refreshInterval]);return useEffect2(()=>(hasDoseSpot&&startTimer(),stopTimer),[hasDoseSpot,startTimer,stopTimer]),unreadCount}import{useMedplum as useMedplum4}from"@medplum/react-hooks";import{useCallback as useCallback3,useState as useState4}from"react";import{addPreferredPharmacyToPatient as addPreferredPharmacyToPatient2,createPreferredPharmacyExtension as createPreferredPharmacyExtension2,getPreferredPharmaciesFromPatient as getPreferredPharmaciesFromPatient2,isAddPharmacyResponse as isAddPharmacyResponse2,isOrganizationArray as isOrganizationArray2,PATIENT_PREFERRED_PHARMACY_URL as PATIENT_PREFERRED_PHARMACY_URL2,PHARMACY_PREFERENCE_TYPE_SYSTEM as PHARMACY_PREFERENCE_TYPE_SYSTEM2,PHARMACY_TYPE_PREFERRED as PHARMACY_TYPE_PREFERRED2,PHARMACY_TYPE_PRIMARY as PHARMACY_TYPE_PRIMARY2,removePreferredPharmacyFromPatient as removePreferredPharmacyFromPatient2}from"@medplum/core";var getMedicationName=medication=>medication?.code?.text||"";function getPharmacyIdFromOrganization(organization){let id=organization.identifier?.find(i=>i.system===DOSESPOT_PHARMACY_ID_SYSTEM)?.value;return id?Number.parseInt(id,10):void 0}function useDoseSpotPatientPharmacy(){let[selectedPharmacy,privateSetSelectedPharmacy]=useState4(void 0),[setAsPrimaryState,privateSetAsPrimary]=useState4(!1),medplum=useMedplum4(),state={selectedPharmacy,setAsPrimary:setAsPrimaryState},searchPharmacies=useCallback3(async params=>await medplum.executeBot(DOSESPOT_SEARCH_PHARMACY_BOT,params),[medplum]),setSelectedPharmacy=pharmacy=>{privateSetSelectedPharmacy(pharmacy)},setAsPrimary=primary=>{privateSetAsPrimary(primary)},addFavoritePharmacy=useCallback3(async patientId=>{if(!selectedPharmacy)throw new Error("Must select a pharmacy before adding it as a favorite");if(!getPharmacyIdFromOrganization(selectedPharmacy))throw new Error("Selected pharmacy does not have a valid DoseSpot pharmacy ID");return medplum.executeBot(DOSESPOT_ADD_PATIENT_PHARMACY_BOT,{patientId,pharmacy:selectedPharmacy,setAsPrimary:setAsPrimaryState})},[selectedPharmacy,setAsPrimaryState,medplum]);return{state,searchPharmacies,setSelectedPharmacy,setAsPrimary,addFavoritePharmacy,clear:()=>{privateSetSelectedPharmacy(void 0),privateSetAsPrimary(!1)}}}import{usePharmacySearch}from"@medplum/react-hooks";function useDoseSpotPharmacySearch(){return usePharmacySearch(DOSESPOT_SEARCH_PHARMACY_BOT,DOSESPOT_ADD_PATIENT_PHARMACY_BOT)}import{useMedplum as useMedplum5}from"@medplum/react-hooks";import{useCallback as useCallback4,useEffect as useEffect3,useRef as useRef3,useState as useState5}from"react";function useDoseSpotSelfEnrollment(options){let medplum=useMedplum5(),enabled=options?.enabled??!0,initializingRef=useRef3(!1),[result,setResult]=useState5(void 0),[loading,setLoading]=useState5(enabled),[error,setError]=useState5(void 0),onSuccessRef=useRef3(options?.onSuccess);onSuccessRef.current=options?.onSuccess;let onErrorRef=useRef3(options?.onError);onErrorRef.current=options?.onError;let enroll=useCallback4(async()=>{if(!(initializingRef.current||!enabled)){initializingRef.current=!0,setLoading(!0),setError(void 0);try{let enrollResult=await medplum.executeBot(DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,{});setResult(enrollResult),onSuccessRef.current?.(enrollResult)}catch(err){setError(err),onErrorRef.current?.(err)}finally{setLoading(!1)}}},[medplum,enabled]);return useEffect3(()=>{enroll().catch(console.error)},[enroll]),{result,loading,error}}export{DOSESPOT_ADD_FAVORITE_MEDICATION_BOT,DOSESPOT_ADD_PATIENT_PHARMACY_BOT,DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM,DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM,DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT,DOSESPOT_IFRAME_BOT,DOSESPOT_MEDICATION_HISTORY_BOT,DOSESPOT_NOTIFICATION_COUNTS_BOT,DOSESPOT_PATIENT_ID_SYSTEM,DOSESPOT_PATIENT_SYNC_BOT,DOSESPOT_PHARMACY_ID_SYSTEM,DOSESPOT_PRESCRIPTIONS_SYNC_BOT,DOSESPOT_SEARCH_MEDICATIONS_BOT,DOSESPOT_SEARCH_PHARMACY_BOT,DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,MEDPLUM_BOT_SYSTEM,PATIENT_PREFERRED_PHARMACY_URL2 as PATIENT_PREFERRED_PHARMACY_URL,PHARMACY_PREFERENCE_TYPE_SYSTEM2 as PHARMACY_PREFERENCE_TYPE_SYSTEM,PHARMACY_TYPE_PREFERRED2 as PHARMACY_TYPE_PREFERRED,PHARMACY_TYPE_PRIMARY2 as PHARMACY_TYPE_PRIMARY,addPreferredPharmacyToPatient2 as addPreferredPharmacyToPatient,createPreferredPharmacyExtension2 as createPreferredPharmacyExtension,getMedicationName,getPharmacyIdFromOrganization,getPreferredPharmaciesFromPatient2 as getPreferredPharmaciesFromPatient,isAddPharmacyResponse2 as isAddPharmacyResponse,isOrganizationArray2 as isOrganizationArray,removePreferredPharmacyFromPatient2 as removePreferredPharmacyFromPatient,useDoseSpotClinicFormulary,useDoseSpotIFrame,useDoseSpotNotifications,useDoseSpotPatientPharmacy,useDoseSpotPharmacySearch,useDoseSpotSelfEnrollment};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/esm/index.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../dosespot-core/src/pharmacy-utils.ts", "../../src/useDoseSpotClinicFormulary.ts", "../../src/useDoseSpotIFrame.ts", "../../src/useDoseSpotNotifications.ts", "../../src/useDoseSpotPatientPharmacy.ts", "../../src/utils.ts", "../../src/useDoseSpotPharmacySearch.ts"],
|
|
4
|
-
"sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Identifier } from '@medplum/fhirtypes';\n\n// DoseSpot-specific pharmacy preference type system\nexport const DOSESPOT_PHARMACY_PREFERENCE_TYPE_SYSTEM = 'https://dosespot.com/pharmacy-preference-type';\n\n// Re-export generic pharmacy utilities from @medplum/core\nexport {\n addPreferredPharmacyToPatient,\n createPreferredPharmacyExtension,\n getPreferredPharmaciesFromPatient,\n isAddPharmacyResponse,\n isOrganizationArray,\n PATIENT_PREFERRED_PHARMACY_URL,\n PHARMACY_PREFERENCE_TYPE_SYSTEM,\n PHARMACY_TYPE_PREFERRED,\n PHARMACY_TYPE_PRIMARY,\n removePreferredPharmacyFromPatient,\n} from '@medplum/core';\nexport type { AddFavoriteParams, AddPharmacyResponse, PharmacySearchParams, PreferredPharmacy } from '@medplum/core';\n\n// Bot system\nexport const MEDPLUM_BOT_SYSTEM = 'https://www.medplum.com/bots';\n\n// DoseSpot identifier systems\nexport const DOSESPOT_PATIENT_ID_SYSTEM = 'https://dosespot.com/patient-id';\nexport const DOSESPOT_PHARMACY_ID_SYSTEM = 'https://dosespot.com/pharmacy-id';\nexport const DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM = 'https://dosespot.com/clinic-favorite-medication-id';\nexport const DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM = 'https://dosespot.com/dispensable-drug-id';\n\n// Bot identifiers - Pharmacy\nexport const DOSESPOT_SEARCH_PHARMACY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-search-pharmacy-bot',\n};\n\nexport const DOSESPOT_ADD_PATIENT_PHARMACY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-add-patient-pharmacy-bot',\n};\n\n// Bot identifiers - Patient and Medications\nexport const DOSESPOT_PATIENT_SYNC_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-patient-sync-bot',\n};\n\nexport const DOSESPOT_IFRAME_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-iframe-bot',\n};\n\nexport const DOSESPOT_ADD_FAVORITE_MEDICATION_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-add-favorite-medication-bot',\n};\n\nexport const DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-get-favorite-medications-bot',\n};\n\nexport const DOSESPOT_SEARCH_MEDICATIONS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-search-medication-bot',\n};\n\nexport const DOSESPOT_MEDICATION_HISTORY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-medication-history-bot',\n};\n\nexport const DOSESPOT_PRESCRIPTIONS_SYNC_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-prescriptions-sync-bot',\n};\n\nexport const DOSESPOT_NOTIFICATION_COUNTS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-notification-counts-bot',\n};\n\n// DoseSpot notification response type\nexport interface DoseSpotNotificationCountsResponse {\n PendingPrescriptionsCount: number;\n PendingRxChangeCount: number;\n RefillRequestsCount: number;\n TransactionErrorsCount: number;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { isCodeableConcept, isCoding } from '@medplum/core';\nimport type { CodeableConcept, Coding, MedicationKnowledge } from '@medplum/fhirtypes';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useState } from 'react';\nimport { DOSESPOT_ADD_FAVORITE_MEDICATION_BOT, DOSESPOT_SEARCH_MEDICATIONS_BOT } from './common';\n\nexport interface DoseSpotClinicFormularyReturn {\n state: DoseSpotClinicFormularyState;\n /**\n * Search for DoseSpot Medications and returns array of temporary MedicationKnowledge objects that are not yet saved to the FHIR server\n */\n readonly searchMedications: (searchTerm: string) => Promise<CodeableConcept[]>;\n /**\n * Set the currently selected medication. Can be set as a CodeableConcept or a Coding, but state is always stored as a CodeableConcept\n */\n readonly setSelectedMedication: (medication: CodeableConcept | Coding | undefined) => void;\n /**\n * Set the directions for the currently selected medication\n */\n readonly setSelectedMedicationDirections: (directions: string | undefined) => void;\n /**\n * Save a DoseSpot Medication to the Clinic's favorites and returns the MedicationKnowledge object that was saved\n */\n readonly saveFavoriteMedication: () => Promise<MedicationKnowledge>;\n /**\n * Clear the state\n */\n readonly clear: () => void;\n}\n\nexport interface DoseSpotClinicFormularyState {\n selectedMedication: CodeableConcept | undefined;\n directions: string | undefined;\n}\n\nexport function useDoseSpotClinicFormulary(): DoseSpotClinicFormularyReturn {\n const [directions, privateSetDirections] = useState<string | undefined>(undefined);\n const [selectedMedication, privateSetSelectedMedication] = useState<CodeableConcept | undefined>(undefined);\n const medplum = useMedplum();\n\n const state: DoseSpotClinicFormularyState = { selectedMedication, directions };\n\n const saveFavoriteMedication = useCallback(async (): Promise<MedicationKnowledge> => {\n if (!selectedMedication) {\n throw new Error('Must select a medication before adding a favorite medication');\n }\n\n //Add the directions to the medicationKnowledge object\n const medicationKnowledgeWithDirections = {\n resourceType: 'MedicationKnowledge',\n code: { ...selectedMedication },\n administrationGuidelines: [\n {\n dosage: [\n {\n dosage: [\n {\n patientInstruction: directions || '',\n },\n ],\n type: {\n coding: [\n {\n system: 'https://dosespot.com/patient-instructions',\n },\n ],\n },\n },\n ],\n },\n ],\n };\n\n return medplum.executeBot(DOSESPOT_ADD_FAVORITE_MEDICATION_BOT, medicationKnowledgeWithDirections);\n }, [selectedMedication, directions, medplum]);\n\n const searchMedications = useCallback(\n async (searchTerm: string): Promise<CodeableConcept[]> => {\n return (await medplum.executeBot(DOSESPOT_SEARCH_MEDICATIONS_BOT, { name: searchTerm })) as CodeableConcept[];\n },\n [medplum]\n );\n\n const setSelectedMedicationDirections = (directions: string | undefined): void => {\n privateSetDirections(directions);\n };\n\n const setSelectedMedication = (medication: CodeableConcept | Coding | undefined): void => {\n let medicationToSet: CodeableConcept | undefined;\n if (isCodeableConcept(medication)) {\n medicationToSet = { ...medication };\n } else if (isCoding(medication)) {\n medicationToSet = {\n text: medication.display || '',\n coding: [medication],\n };\n }\n privateSetSelectedMedication(medicationToSet);\n };\n\n const clear = (): void => {\n privateSetSelectedMedication(undefined);\n privateSetDirections(undefined);\n };\n\n return {\n state,\n searchMedications,\n setSelectedMedication,\n setSelectedMedicationDirections,\n saveFavoriteMedication,\n clear,\n };\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { EPrescribingIFrameOptions } from '@medplum/react-hooks';\nimport { useEPrescribingIFrame } from '@medplum/react-hooks';\nimport { DOSESPOT_IFRAME_BOT, DOSESPOT_PATIENT_SYNC_BOT } from './common';\n\nexport type DoseSpotIFrameOptions = EPrescribingIFrameOptions;\n\n/**\n * React hook that syncs a patient to DoseSpot and returns the iframe URL.\n *\n * Thin wrapper around the generic {@link useEPrescribingIFrame} hook,\n * pre-configured with DoseSpot bot identifiers.\n *\n * @param options - Configuration and callback options.\n * @returns The DoseSpot iframe URL, or undefined while loading.\n */\nexport function useDoseSpotIFrame(options: DoseSpotIFrameOptions): string | undefined {\n return useEPrescribingIFrame(DOSESPOT_PATIENT_SYNC_BOT, DOSESPOT_IFRAME_BOT, options);\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { DoseSpotNotificationCountsResponse } from './common';\nimport { DOSESPOT_NOTIFICATION_COUNTS_BOT } from './common';\n\nexport interface DoseSpotNotificationsOptions {\n readonly refreshIntervalMilliseconds?: number;\n readonly onChange?: (count: number) => void;\n readonly onError?: (err: unknown) => void;\n}\n\nconst DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 10000;\n\nexport function useDoseSpotNotifications(options?: DoseSpotNotificationsOptions): number | undefined {\n const medplum = useMedplum();\n const { onChange, onError } = options ?? {};\n const hasDoseSpot = medplum.getProjectMembership()?.identifier?.some((i) => i.system?.includes('dosespot'));\n const refreshInterval = options?.refreshIntervalMilliseconds ?? DEFAULT_REFRESH_INTERVAL_MILLISECONDS;\n const timerRef = useRef<NodeJS.Timeout | undefined>(undefined);\n const [unreadCount, setUnreadCount] = useState<number | undefined>(undefined);\n\n const stopTimer = useCallback(() => {\n const timerId = timerRef.current;\n if (timerId) {\n clearInterval(timerId);\n }\n }, []);\n\n const updateCount = useCallback(async () => {\n try {\n const result = (await medplum.executeBot(\n DOSESPOT_NOTIFICATION_COUNTS_BOT,\n {}\n )) as DoseSpotNotificationCountsResponse;\n\n let newCount = 0;\n if (result.PendingPrescriptionsCount) {\n newCount += result.PendingPrescriptionsCount;\n }\n if (result.PendingRxChangeCount) {\n newCount += result.PendingRxChangeCount;\n }\n if (result.RefillRequestsCount) {\n newCount += result.RefillRequestsCount;\n }\n if (result.TransactionErrorsCount) {\n newCount += result.TransactionErrorsCount;\n }\n if (newCount !== unreadCount) {\n setUnreadCount(newCount);\n onChange?.(newCount);\n }\n } catch (err: unknown) {\n onError?.(err);\n stopTimer();\n }\n }, [medplum, unreadCount, onChange, onError, stopTimer]);\n\n const startTimer = useCallback(() => {\n timerRef.current = setInterval(() => {\n updateCount().catch(console.error);\n }, refreshInterval);\n }, [updateCount, refreshInterval]);\n\n useEffect(() => {\n // Start an interval timer to update the count every 5 seconds\n if (hasDoseSpot) {\n startTimer();\n }\n\n // Clear the interval timer when the component is unmounted\n return stopTimer;\n }, [hasDoseSpot, startTimer, stopTimer]);\n\n return unreadCount;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { Organization } from '@medplum/fhirtypes';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useState } from 'react';\nimport { DOSESPOT_ADD_PATIENT_PHARMACY_BOT, DOSESPOT_SEARCH_PHARMACY_BOT } from './common';\nimport { getPharmacyIdFromOrganization } from './utils';\n\n/**\n * Search parameters for pharmacy search.\n * See DoseSpot API section 3.13.2.\n */\nexport interface PharmacySearchParams {\n /** Pharmacy's store name (min 3 chars) */\n name?: string;\n /** City (min 3 chars) */\n city?: string;\n /** State (min 3 chars) */\n state?: string;\n /** Zip code (min 3 chars) */\n zip?: string;\n /** Address (min 3 chars) */\n address?: string;\n /** Phone or fax number */\n phoneOrFax?: string;\n /** Collection of pharmacy specialties (numeric values) */\n specialty?: number[];\n /** National Council for Prescription Drug Programs Identifier */\n ncpdpID?: string;\n /** Page number of results (defaults to 1) */\n pageNumber?: number;\n}\n\n/**\n * Response from adding a pharmacy to a patient's favorites.\n */\nexport interface AddPatientPharmacyResponse {\n /** Whether the operation was successful */\n success: boolean;\n /** A message describing the result */\n message: string;\n /** The persisted Organization resource with ID */\n organization?: Organization;\n}\n\n/**\n * State managed by the useDoseSpotPatientPharmacy hook.\n */\nexport interface DoseSpotPatientPharmacyState {\n /** The currently selected pharmacy Organization */\n selectedPharmacy: Organization | undefined;\n /** Whether to set the selected pharmacy as the patient's primary pharmacy */\n setAsPrimary: boolean;\n}\n\n/**\n * Return type for the useDoseSpotPatientPharmacy hook.\n */\nexport interface DoseSpotPatientPharmacyReturn {\n /** Current state of the hook */\n state: DoseSpotPatientPharmacyState;\n /**\n * Search for pharmacies in DoseSpot.\n * Returns synthetic Organization resources (not persisted to DB).\n */\n readonly searchPharmacies: (params: PharmacySearchParams) => Promise<Organization[]>;\n /**\n * Set the currently selected pharmacy.\n */\n readonly setSelectedPharmacy: (pharmacy: Organization | undefined) => void;\n /**\n * Set whether to add the pharmacy as the patient's primary pharmacy.\n */\n readonly setAsPrimary: (primary: boolean) => void;\n /**\n * Add the selected pharmacy to the patient's favorites in DoseSpot.\n * @param patientId - The Medplum Patient ID\n */\n readonly addFavoritePharmacy: (patientId: string) => Promise<AddPatientPharmacyResponse>;\n /**\n * Clear the state (selected pharmacy and setAsPrimary flag).\n */\n readonly clear: () => void;\n}\n\n/**\n * React hook for searching DoseSpot pharmacies and adding them to patient favorites.\n *\n * @returns The hook return object with state and methods.\n *\n * @example\n * ```tsx\n * function PharmacySearch({ patientId }: { patientId: string }) {\n * const {\n * state,\n * searchPharmacies,\n * setSelectedPharmacy,\n * setAsPrimary,\n * addFavoritePharmacy,\n * clear\n * } = useDoseSpotPatientPharmacy();\n *\n * const handleSearch = async () => {\n * const results = await searchPharmacies({ zip: '94118' });\n * // Display results to user\n * };\n *\n * const handleAddFavorite = async () => {\n * const result = await addFavoritePharmacy(patientId);\n * if (result.success) {\n * clear();\n * }\n * };\n * }\n * ```\n */\nexport function useDoseSpotPatientPharmacy(): DoseSpotPatientPharmacyReturn {\n const [selectedPharmacy, privateSetSelectedPharmacy] = useState<Organization | undefined>(undefined);\n const [setAsPrimaryState, privateSetAsPrimary] = useState(false);\n const medplum = useMedplum();\n\n const state: DoseSpotPatientPharmacyState = {\n selectedPharmacy,\n setAsPrimary: setAsPrimaryState,\n };\n\n const searchPharmacies = useCallback(\n async (params: PharmacySearchParams): Promise<Organization[]> => {\n return (await medplum.executeBot(DOSESPOT_SEARCH_PHARMACY_BOT, params)) as Organization[];\n },\n [medplum]\n );\n\n const setSelectedPharmacy = (pharmacy: Organization | undefined): void => {\n privateSetSelectedPharmacy(pharmacy);\n };\n\n const setAsPrimary = (primary: boolean): void => {\n privateSetAsPrimary(primary);\n };\n\n const addFavoritePharmacy = useCallback(\n async (patientId: string): Promise<AddPatientPharmacyResponse> => {\n if (!selectedPharmacy) {\n throw new Error('Must select a pharmacy before adding it as a favorite');\n }\n\n const pharmacyId = getPharmacyIdFromOrganization(selectedPharmacy);\n if (!pharmacyId) {\n throw new Error('Selected pharmacy does not have a valid DoseSpot pharmacy ID');\n }\n\n return medplum.executeBot(DOSESPOT_ADD_PATIENT_PHARMACY_BOT, {\n patientId,\n pharmacy: selectedPharmacy,\n setAsPrimary: setAsPrimaryState,\n }) as Promise<AddPatientPharmacyResponse>;\n },\n [selectedPharmacy, setAsPrimaryState, medplum]\n );\n\n const clear = (): void => {\n privateSetSelectedPharmacy(undefined);\n privateSetAsPrimary(false);\n };\n\n return {\n state,\n searchPharmacies,\n setSelectedPharmacy,\n setAsPrimary,\n addFavoritePharmacy,\n clear,\n };\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { MedicationKnowledge, Organization } from '@medplum/fhirtypes';\nimport { DOSESPOT_PHARMACY_ID_SYSTEM } from './common';\n\nexport const getMedicationName = (medication: MedicationKnowledge | undefined): string => {\n return medication?.code?.text || '';\n};\n\n/**\n * Extracts the DoseSpot pharmacy ID from an Organization resource.\n * @param organization - The FHIR Organization resource representing a pharmacy.\n * @returns The DoseSpot pharmacy ID, or undefined if not found.\n */\nexport function getPharmacyIdFromOrganization(organization: Organization): number | undefined {\n const id = organization.identifier?.find((i) => i.system === DOSESPOT_PHARMACY_ID_SYSTEM)?.value;\n return id ? Number.parseInt(id, 10) : undefined;\n}\n\nexport {\n addPreferredPharmacyToPatient,\n createPreferredPharmacyExtension,\n getPreferredPharmaciesFromPatient,\n isAddPharmacyResponse,\n isOrganizationArray,\n PATIENT_PREFERRED_PHARMACY_URL,\n PHARMACY_PREFERENCE_TYPE_SYSTEM,\n PHARMACY_TYPE_PREFERRED,\n PHARMACY_TYPE_PRIMARY,\n removePreferredPharmacyFromPatient,\n} from '@medplum/core';\nexport type { PreferredPharmacy } from '@medplum/core';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { UsePharmacySearchReturn } from '@medplum/react-hooks';\nimport { usePharmacySearch } from '@medplum/react-hooks';\nimport { DOSESPOT_ADD_PATIENT_PHARMACY_BOT, DOSESPOT_SEARCH_PHARMACY_BOT } from './common';\n\nexport type UseDoseSpotPharmacySearchReturn = UsePharmacySearchReturn;\n\n/**\n * React hook that provides DoseSpot-specific pharmacy search and add-to-favorites functionality.\n *\n * Thin wrapper around the generic {@link usePharmacySearch} hook,\n * pre-configured with DoseSpot bot identifiers.\n *\n * @returns An object with `searchPharmacies` and `addToFavorites` callbacks.\n */\nexport function useDoseSpotPharmacySearch(): UseDoseSpotPharmacySearchReturn {\n return usePharmacySearch(DOSESPOT_SEARCH_PHARMACY_BOT, DOSESPOT_ADD_PATIENT_PHARMACY_BOT);\n}\n"],
|
|
5
|
-
"mappings": "AASA,OACE,8BACA,iCACA,kCACA,sBACA,oBACA,+BACA,gCACA,wBACA,sBACA,uCACK,gBAdA,IAkBM,mBAAqB,+BAGrB,2BAA6B,kCAC7B,4BAA8B,mCAC9B,mCAAqC,qDACrC,oCAAsC,2CAGtC,6BAA2C,CACtD,OAAQ,mBACR,MAAO,8BACT,EAEa,kCAAgD,CAC3D,OAAQ,mBACR,MAAO,mCACT,EAGa,0BAAwC,CACnD,OAAQ,mBACR,MAAO,2BACT,EAEa,oBAAkC,CAC7C,OAAQ,mBACR,MAAO,qBACT,EAEa,qCAAmD,CAC9D,OAAQ,mBACR,MAAO,sCACT,EAEa,sCAAoD,CAC/D,OAAQ,mBACR,MAAO,uCACT,EAEa,gCAA8C,CACzD,OAAQ,mBACR,MAAO,gCACT,EAEa,gCAA8C,CACzD,OAAQ,mBACR,MAAO,iCACT,EAEa,gCAA8C,CACzD,OAAQ,mBACR,MAAO,iCACT,EAEa,iCAA+C,CAC1D,OAAQ,mBACR,MAAO,kCACT,
|
|
6
|
-
"names": ["directions", "useMedplum", "useCallback", "useState", "useMedplum", "useState", "useCallback", "useMedplum", "useCallback", "useState", "addPreferredPharmacyToPatient", "createPreferredPharmacyExtension", "getPreferredPharmaciesFromPatient", "isAddPharmacyResponse", "isOrganizationArray", "PATIENT_PREFERRED_PHARMACY_URL", "PHARMACY_PREFERENCE_TYPE_SYSTEM", "PHARMACY_TYPE_PREFERRED", "PHARMACY_TYPE_PRIMARY", "removePreferredPharmacyFromPatient", "useState", "useMedplum", "useCallback"]
|
|
3
|
+
"sources": ["../../../dosespot-core/src/pharmacy-utils.ts", "../../src/useDoseSpotClinicFormulary.ts", "../../src/useDoseSpotIFrame.ts", "../../src/useDoseSpotNotifications.ts", "../../src/useDoseSpotPatientPharmacy.ts", "../../src/utils.ts", "../../src/useDoseSpotPharmacySearch.ts", "../../src/useDoseSpotSelfEnrollment.ts"],
|
|
4
|
+
"sourcesContent": ["// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Identifier } from '@medplum/fhirtypes';\n\n// DoseSpot-specific pharmacy preference type system\nexport const DOSESPOT_PHARMACY_PREFERENCE_TYPE_SYSTEM = 'https://dosespot.com/pharmacy-preference-type';\n\n// Re-export generic pharmacy utilities from @medplum/core\nexport {\n addPreferredPharmacyToPatient,\n createPreferredPharmacyExtension,\n getPreferredPharmaciesFromPatient,\n isAddPharmacyResponse,\n isOrganizationArray,\n PATIENT_PREFERRED_PHARMACY_URL,\n PHARMACY_PREFERENCE_TYPE_SYSTEM,\n PHARMACY_TYPE_PREFERRED,\n PHARMACY_TYPE_PRIMARY,\n removePreferredPharmacyFromPatient,\n} from '@medplum/core';\nexport type { AddFavoriteParams, AddPharmacyResponse, PharmacySearchParams, PreferredPharmacy } from '@medplum/core';\n\n// Bot system\nexport const MEDPLUM_BOT_SYSTEM = 'https://www.medplum.com/bots';\n\n// DoseSpot identifier systems\nexport const DOSESPOT_PATIENT_ID_SYSTEM = 'https://dosespot.com/patient-id';\nexport const DOSESPOT_PHARMACY_ID_SYSTEM = 'https://dosespot.com/pharmacy-id';\nexport const DOSESPOT_CLINIC_FAVORITE_ID_SYSTEM = 'https://dosespot.com/clinic-favorite-medication-id';\nexport const DOSESPOT_DISPENSABLE_DRUG_ID_SYSTEM = 'https://dosespot.com/dispensable-drug-id';\n\n// Bot identifiers - Pharmacy\nexport const DOSESPOT_SEARCH_PHARMACY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-search-pharmacy-bot',\n};\n\nexport const DOSESPOT_ADD_PATIENT_PHARMACY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-add-patient-pharmacy-bot',\n};\n\n// Bot identifiers - Patient and Medications\nexport const DOSESPOT_PATIENT_SYNC_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-patient-sync-bot',\n};\n\nexport const DOSESPOT_IFRAME_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-iframe-bot',\n};\n\nexport const DOSESPOT_ADD_FAVORITE_MEDICATION_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-add-favorite-medication-bot',\n};\n\nexport const DOSESPOT_GET_FAVORITE_MEDICATIONS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-get-favorite-medications-bot',\n};\n\nexport const DOSESPOT_SEARCH_MEDICATIONS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-search-medication-bot',\n};\n\nexport const DOSESPOT_MEDICATION_HISTORY_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-medication-history-bot',\n};\n\nexport const DOSESPOT_PRESCRIPTIONS_SYNC_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-prescriptions-sync-bot',\n};\n\nexport const DOSESPOT_NOTIFICATION_COUNTS_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-notification-counts-bot',\n};\n\n// Bot identifiers - Enrollment\nexport const DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT: Identifier = {\n system: MEDPLUM_BOT_SYSTEM,\n value: 'dosespot-self-enroll-prescriber-bot',\n};\n\n// DoseSpot notification response type\nexport interface DoseSpotNotificationCountsResponse {\n PendingPrescriptionsCount: number;\n PendingRxChangeCount: number;\n RefillRequestsCount: number;\n TransactionErrorsCount: number;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { isCodeableConcept, isCoding } from '@medplum/core';\nimport type { CodeableConcept, Coding, MedicationKnowledge } from '@medplum/fhirtypes';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useState } from 'react';\nimport { DOSESPOT_ADD_FAVORITE_MEDICATION_BOT, DOSESPOT_SEARCH_MEDICATIONS_BOT } from './common';\n\nexport interface DoseSpotClinicFormularyReturn {\n state: DoseSpotClinicFormularyState;\n /**\n * Search for DoseSpot Medications and returns array of temporary MedicationKnowledge objects that are not yet saved to the FHIR server\n */\n readonly searchMedications: (searchTerm: string) => Promise<CodeableConcept[]>;\n /**\n * Set the currently selected medication. Can be set as a CodeableConcept or a Coding, but state is always stored as a CodeableConcept\n */\n readonly setSelectedMedication: (medication: CodeableConcept | Coding | undefined) => void;\n /**\n * Set the directions for the currently selected medication\n */\n readonly setSelectedMedicationDirections: (directions: string | undefined) => void;\n /**\n * Save a DoseSpot Medication to the Clinic's favorites and returns the MedicationKnowledge object that was saved\n */\n readonly saveFavoriteMedication: () => Promise<MedicationKnowledge>;\n /**\n * Clear the state\n */\n readonly clear: () => void;\n}\n\nexport interface DoseSpotClinicFormularyState {\n selectedMedication: CodeableConcept | undefined;\n directions: string | undefined;\n}\n\nexport function useDoseSpotClinicFormulary(): DoseSpotClinicFormularyReturn {\n const [directions, privateSetDirections] = useState<string | undefined>(undefined);\n const [selectedMedication, privateSetSelectedMedication] = useState<CodeableConcept | undefined>(undefined);\n const medplum = useMedplum();\n\n const state: DoseSpotClinicFormularyState = { selectedMedication, directions };\n\n const saveFavoriteMedication = useCallback(async (): Promise<MedicationKnowledge> => {\n if (!selectedMedication) {\n throw new Error('Must select a medication before adding a favorite medication');\n }\n\n //Add the directions to the medicationKnowledge object\n const medicationKnowledgeWithDirections = {\n resourceType: 'MedicationKnowledge',\n code: { ...selectedMedication },\n administrationGuidelines: [\n {\n dosage: [\n {\n dosage: [\n {\n patientInstruction: directions || '',\n },\n ],\n type: {\n coding: [\n {\n system: 'https://dosespot.com/patient-instructions',\n },\n ],\n },\n },\n ],\n },\n ],\n };\n\n return medplum.executeBot(DOSESPOT_ADD_FAVORITE_MEDICATION_BOT, medicationKnowledgeWithDirections);\n }, [selectedMedication, directions, medplum]);\n\n const searchMedications = useCallback(\n async (searchTerm: string): Promise<CodeableConcept[]> => {\n return (await medplum.executeBot(DOSESPOT_SEARCH_MEDICATIONS_BOT, { name: searchTerm })) as CodeableConcept[];\n },\n [medplum]\n );\n\n const setSelectedMedicationDirections = (directions: string | undefined): void => {\n privateSetDirections(directions);\n };\n\n const setSelectedMedication = (medication: CodeableConcept | Coding | undefined): void => {\n let medicationToSet: CodeableConcept | undefined;\n if (isCodeableConcept(medication)) {\n medicationToSet = { ...medication };\n } else if (isCoding(medication)) {\n medicationToSet = {\n text: medication.display || '',\n coding: [medication],\n };\n }\n privateSetSelectedMedication(medicationToSet);\n };\n\n const clear = (): void => {\n privateSetSelectedMedication(undefined);\n privateSetDirections(undefined);\n };\n\n return {\n state,\n searchMedications,\n setSelectedMedication,\n setSelectedMedicationDirections,\n saveFavoriteMedication,\n clear,\n };\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { ProjectMembership } from '@medplum/fhirtypes';\nimport type { EPrescribingIFrameOptions } from '@medplum/react-hooks';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useEffect, useRef, useState } from 'react';\nimport { DOSESPOT_IFRAME_BOT, DOSESPOT_PATIENT_SYNC_BOT, DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT } from './common';\nimport type { DoseSpotSelfEnrollmentResult } from './useDoseSpotSelfEnrollment';\n\nexport interface DoseSpotIFrameOptions extends EPrescribingIFrameOptions {\n /**\n * When true, automatically runs the self-enrollment bot before loading\n * the iframe if the current user does not have a DoseSpot identifier\n * on their ProjectMembership. Requires an active PractitionerRole with\n * DoseSpot role type codes for the practitioner.\n */\n readonly selfEnroll?: boolean;\n /** Called after self-enrollment completes successfully. */\n readonly onSelfEnrollSuccess?: (result: DoseSpotSelfEnrollmentResult) => void;\n}\n\n/**\n * React hook that syncs a patient to DoseSpot and returns the iframe URL.\n *\n * Runs optional self-enrollment, then the patient-sync bot (when `patientId`\n * is set), then the iframe bot \u2014 aligned with {@link useEPrescribingIFrame}\n * behavior plus DoseSpot-specific enrollment.\n *\n * @param options - Configuration and callback options.\n * @returns The DoseSpot iframe URL, or undefined while loading.\n */\nexport function useDoseSpotIFrame(options: DoseSpotIFrameOptions): string | undefined {\n const medplum = useMedplum();\n const { patientId, selfEnroll, onPatientSyncSuccess, onIframeSuccess, onSelfEnrollSuccess, onError } = options;\n const [iframeUrl, setIframeUrl] = useState<string | undefined>(undefined);\n\n const onPatientSyncSuccessRef = useRef(onPatientSyncSuccess);\n const onIframeSuccessRef = useRef(onIframeSuccess);\n const onSelfEnrollSuccessRef = useRef(onSelfEnrollSuccess);\n const onErrorRef = useRef(onError);\n\n useEffect(() => {\n onPatientSyncSuccessRef.current = onPatientSyncSuccess;\n onIframeSuccessRef.current = onIframeSuccess;\n onSelfEnrollSuccessRef.current = onSelfEnrollSuccess;\n onErrorRef.current = onError;\n }, [onPatientSyncSuccess, onIframeSuccess, onSelfEnrollSuccess, onError]);\n\n useEffect(() => {\n let cancelled = false;\n\n const run = async (): Promise<void> => {\n if (selfEnroll && !hasDoseSpotIdentifier(medplum.getProjectMembership())) {\n const enrollResult = (await medplum.executeBot(\n DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,\n {}\n )) as DoseSpotSelfEnrollmentResult;\n if (cancelled) {\n return;\n }\n onSelfEnrollSuccessRef.current?.(enrollResult);\n }\n\n if (patientId) {\n await medplum.executeBot(DOSESPOT_PATIENT_SYNC_BOT, { patientId });\n if (cancelled) {\n return;\n }\n onPatientSyncSuccessRef.current?.();\n }\n const result = await medplum.executeBot(DOSESPOT_IFRAME_BOT, { patientId });\n if (cancelled) {\n return;\n }\n if (result.url) {\n setIframeUrl(result.url);\n onIframeSuccessRef.current?.(result.url);\n }\n };\n\n run().catch((err: unknown) => {\n if (!cancelled) {\n onErrorRef.current?.(err);\n }\n });\n\n return (): void => {\n cancelled = true;\n };\n }, [medplum, patientId, selfEnroll]);\n\n return iframeUrl;\n}\n\n/**\n * Checks whether a ProjectMembership has a DoseSpot identifier.\n *\n * @param membership - The project membership to check.\n * @returns True when membership identifiers include a DoseSpot system URL.\n */\nfunction hasDoseSpotIdentifier(membership: ProjectMembership | undefined): boolean {\n return !!membership?.identifier?.some((i) => i.system?.includes('dosespot'));\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { DoseSpotNotificationCountsResponse } from './common';\nimport { DOSESPOT_NOTIFICATION_COUNTS_BOT } from './common';\n\nexport interface DoseSpotNotificationsOptions {\n readonly refreshIntervalMilliseconds?: number;\n readonly onChange?: (count: number) => void;\n readonly onError?: (err: unknown) => void;\n}\n\nconst DEFAULT_REFRESH_INTERVAL_MILLISECONDS = 10000;\n\nexport function useDoseSpotNotifications(options?: DoseSpotNotificationsOptions): number | undefined {\n const medplum = useMedplum();\n const { onChange, onError } = options ?? {};\n const hasDoseSpot = medplum.getProjectMembership()?.identifier?.some((i) => i.system?.includes('dosespot'));\n const refreshInterval = options?.refreshIntervalMilliseconds ?? DEFAULT_REFRESH_INTERVAL_MILLISECONDS;\n const timerRef = useRef<NodeJS.Timeout | undefined>(undefined);\n const [unreadCount, setUnreadCount] = useState<number | undefined>(undefined);\n\n const stopTimer = useCallback(() => {\n const timerId = timerRef.current;\n if (timerId) {\n clearInterval(timerId);\n }\n }, []);\n\n const updateCount = useCallback(async () => {\n try {\n const result = (await medplum.executeBot(\n DOSESPOT_NOTIFICATION_COUNTS_BOT,\n {}\n )) as DoseSpotNotificationCountsResponse;\n\n let newCount = 0;\n if (result.PendingPrescriptionsCount) {\n newCount += result.PendingPrescriptionsCount;\n }\n if (result.PendingRxChangeCount) {\n newCount += result.PendingRxChangeCount;\n }\n if (result.RefillRequestsCount) {\n newCount += result.RefillRequestsCount;\n }\n if (result.TransactionErrorsCount) {\n newCount += result.TransactionErrorsCount;\n }\n if (newCount !== unreadCount) {\n setUnreadCount(newCount);\n onChange?.(newCount);\n }\n } catch (err: unknown) {\n onError?.(err);\n stopTimer();\n }\n }, [medplum, unreadCount, onChange, onError, stopTimer]);\n\n const startTimer = useCallback(() => {\n timerRef.current = setInterval(() => {\n updateCount().catch(console.error);\n }, refreshInterval);\n }, [updateCount, refreshInterval]);\n\n useEffect(() => {\n // Start an interval timer to update the count every 5 seconds\n if (hasDoseSpot) {\n startTimer();\n }\n\n // Clear the interval timer when the component is unmounted\n return stopTimer;\n }, [hasDoseSpot, startTimer, stopTimer]);\n\n return unreadCount;\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { Organization } from '@medplum/fhirtypes';\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useState } from 'react';\nimport { DOSESPOT_ADD_PATIENT_PHARMACY_BOT, DOSESPOT_SEARCH_PHARMACY_BOT } from './common';\nimport { getPharmacyIdFromOrganization } from './utils';\n\n/**\n * Search parameters for pharmacy search.\n * See DoseSpot API section 3.13.2.\n */\nexport interface PharmacySearchParams {\n /** Pharmacy's store name (min 3 chars) */\n name?: string;\n /** City (min 3 chars) */\n city?: string;\n /** State (min 3 chars) */\n state?: string;\n /** Zip code (min 3 chars) */\n zip?: string;\n /** Address (min 3 chars) */\n address?: string;\n /** Phone or fax number */\n phoneOrFax?: string;\n /** Collection of pharmacy specialties (numeric values) */\n specialty?: number[];\n /** National Council for Prescription Drug Programs Identifier */\n ncpdpID?: string;\n /** Page number of results (defaults to 1) */\n pageNumber?: number;\n}\n\n/**\n * Response from adding a pharmacy to a patient's favorites.\n */\nexport interface AddPatientPharmacyResponse {\n /** Whether the operation was successful */\n success: boolean;\n /** A message describing the result */\n message: string;\n /** The persisted Organization resource with ID */\n organization?: Organization;\n}\n\n/**\n * State managed by the useDoseSpotPatientPharmacy hook.\n */\nexport interface DoseSpotPatientPharmacyState {\n /** The currently selected pharmacy Organization */\n selectedPharmacy: Organization | undefined;\n /** Whether to set the selected pharmacy as the patient's primary pharmacy */\n setAsPrimary: boolean;\n}\n\n/**\n * Return type for the useDoseSpotPatientPharmacy hook.\n */\nexport interface DoseSpotPatientPharmacyReturn {\n /** Current state of the hook */\n state: DoseSpotPatientPharmacyState;\n /**\n * Search for pharmacies in DoseSpot.\n * Returns synthetic Organization resources (not persisted to DB).\n */\n readonly searchPharmacies: (params: PharmacySearchParams) => Promise<Organization[]>;\n /**\n * Set the currently selected pharmacy.\n */\n readonly setSelectedPharmacy: (pharmacy: Organization | undefined) => void;\n /**\n * Set whether to add the pharmacy as the patient's primary pharmacy.\n */\n readonly setAsPrimary: (primary: boolean) => void;\n /**\n * Add the selected pharmacy to the patient's favorites in DoseSpot.\n * @param patientId - The Medplum Patient ID\n */\n readonly addFavoritePharmacy: (patientId: string) => Promise<AddPatientPharmacyResponse>;\n /**\n * Clear the state (selected pharmacy and setAsPrimary flag).\n */\n readonly clear: () => void;\n}\n\n/**\n * React hook for searching DoseSpot pharmacies and adding them to patient favorites.\n *\n * @returns The hook return object with state and methods.\n *\n * @example\n * ```tsx\n * function PharmacySearch({ patientId }: { patientId: string }) {\n * const {\n * state,\n * searchPharmacies,\n * setSelectedPharmacy,\n * setAsPrimary,\n * addFavoritePharmacy,\n * clear\n * } = useDoseSpotPatientPharmacy();\n *\n * const handleSearch = async () => {\n * const results = await searchPharmacies({ zip: '94118' });\n * // Display results to user\n * };\n *\n * const handleAddFavorite = async () => {\n * const result = await addFavoritePharmacy(patientId);\n * if (result.success) {\n * clear();\n * }\n * };\n * }\n * ```\n */\nexport function useDoseSpotPatientPharmacy(): DoseSpotPatientPharmacyReturn {\n const [selectedPharmacy, privateSetSelectedPharmacy] = useState<Organization | undefined>(undefined);\n const [setAsPrimaryState, privateSetAsPrimary] = useState(false);\n const medplum = useMedplum();\n\n const state: DoseSpotPatientPharmacyState = {\n selectedPharmacy,\n setAsPrimary: setAsPrimaryState,\n };\n\n const searchPharmacies = useCallback(\n async (params: PharmacySearchParams): Promise<Organization[]> => {\n return (await medplum.executeBot(DOSESPOT_SEARCH_PHARMACY_BOT, params)) as Organization[];\n },\n [medplum]\n );\n\n const setSelectedPharmacy = (pharmacy: Organization | undefined): void => {\n privateSetSelectedPharmacy(pharmacy);\n };\n\n const setAsPrimary = (primary: boolean): void => {\n privateSetAsPrimary(primary);\n };\n\n const addFavoritePharmacy = useCallback(\n async (patientId: string): Promise<AddPatientPharmacyResponse> => {\n if (!selectedPharmacy) {\n throw new Error('Must select a pharmacy before adding it as a favorite');\n }\n\n const pharmacyId = getPharmacyIdFromOrganization(selectedPharmacy);\n if (!pharmacyId) {\n throw new Error('Selected pharmacy does not have a valid DoseSpot pharmacy ID');\n }\n\n return medplum.executeBot(DOSESPOT_ADD_PATIENT_PHARMACY_BOT, {\n patientId,\n pharmacy: selectedPharmacy,\n setAsPrimary: setAsPrimaryState,\n }) as Promise<AddPatientPharmacyResponse>;\n },\n [selectedPharmacy, setAsPrimaryState, medplum]\n );\n\n const clear = (): void => {\n privateSetSelectedPharmacy(undefined);\n privateSetAsPrimary(false);\n };\n\n return {\n state,\n searchPharmacies,\n setSelectedPharmacy,\n setAsPrimary,\n addFavoritePharmacy,\n clear,\n };\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport type { MedicationKnowledge, Organization } from '@medplum/fhirtypes';\nimport { DOSESPOT_PHARMACY_ID_SYSTEM } from './common';\n\nexport const getMedicationName = (medication: MedicationKnowledge | undefined): string => {\n return medication?.code?.text || '';\n};\n\n/**\n * Extracts the DoseSpot pharmacy ID from an Organization resource.\n * @param organization - The FHIR Organization resource representing a pharmacy.\n * @returns The DoseSpot pharmacy ID, or undefined if not found.\n */\nexport function getPharmacyIdFromOrganization(organization: Organization): number | undefined {\n const id = organization.identifier?.find((i) => i.system === DOSESPOT_PHARMACY_ID_SYSTEM)?.value;\n return id ? Number.parseInt(id, 10) : undefined;\n}\n\nexport {\n addPreferredPharmacyToPatient,\n createPreferredPharmacyExtension,\n getPreferredPharmaciesFromPatient,\n isAddPharmacyResponse,\n isOrganizationArray,\n PATIENT_PREFERRED_PHARMACY_URL,\n PHARMACY_PREFERENCE_TYPE_SYSTEM,\n PHARMACY_TYPE_PREFERRED,\n PHARMACY_TYPE_PRIMARY,\n removePreferredPharmacyFromPatient,\n} from '@medplum/core';\nexport type { PreferredPharmacy } from '@medplum/core';\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { UsePharmacySearchReturn } from '@medplum/react-hooks';\nimport { usePharmacySearch } from '@medplum/react-hooks';\nimport { DOSESPOT_ADD_PATIENT_PHARMACY_BOT, DOSESPOT_SEARCH_PHARMACY_BOT } from './common';\n\nexport type UseDoseSpotPharmacySearchReturn = UsePharmacySearchReturn;\n\n/**\n * React hook that provides DoseSpot-specific pharmacy search and add-to-favorites functionality.\n *\n * Thin wrapper around the generic {@link usePharmacySearch} hook,\n * pre-configured with DoseSpot bot identifiers.\n *\n * @returns An object with `searchPharmacies` and `addToFavorites` callbacks.\n */\nexport function useDoseSpotPharmacySearch(): UseDoseSpotPharmacySearchReturn {\n return usePharmacySearch(DOSESPOT_SEARCH_PHARMACY_BOT, DOSESPOT_ADD_PATIENT_PHARMACY_BOT);\n}\n", "// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors\n// SPDX-License-Identifier: Apache-2.0\nimport { useMedplum } from '@medplum/react-hooks';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT } from './common';\n\n/**\n * Result returned by the DoseSpot self-enrollment bot.\n */\nexport interface DoseSpotSelfEnrollmentResult {\n status: 'created' | 'already_enrolled' | 'advanced';\n doseSpotClinicianId: number;\n registrationStatus: string;\n epcsEnabled: boolean;\n nextSteps: string[];\n}\n\n/**\n * Options for the {@link useDoseSpotSelfEnrollment} hook.\n */\nexport interface DoseSpotSelfEnrollmentOptions {\n /** Whether to run the self-enrollment bot. Defaults to true. */\n readonly enabled?: boolean;\n /** Called when enrollment succeeds. */\n readonly onSuccess?: (result: DoseSpotSelfEnrollmentResult) => void;\n /** Called when enrollment fails. */\n readonly onError?: (err: unknown) => void;\n}\n\n/**\n * React hook that executes the DoseSpot self-enrollment bot.\n *\n * Runs the `dosespot-self-enroll-prescriber-bot` once on mount (when enabled)\n * and returns the enrollment result, loading state, and any error.\n *\n * The bot is idempotent -- it creates the clinician on the first call and\n * auto-advances through registration stages (IDP, TFA) on subsequent calls.\n *\n * @param options - Configuration options.\n * @returns An object with `result`, `loading`, and `error` fields.\n */\nexport function useDoseSpotSelfEnrollment(options?: DoseSpotSelfEnrollmentOptions): {\n result: DoseSpotSelfEnrollmentResult | undefined;\n loading: boolean;\n error: unknown;\n} {\n const medplum = useMedplum();\n const enabled = options?.enabled ?? true;\n const initializingRef = useRef(false);\n const [result, setResult] = useState<DoseSpotSelfEnrollmentResult | undefined>(undefined);\n const [loading, setLoading] = useState(enabled);\n const [error, setError] = useState<unknown>(undefined);\n\n const onSuccessRef = useRef(options?.onSuccess);\n onSuccessRef.current = options?.onSuccess;\n\n const onErrorRef = useRef(options?.onError);\n onErrorRef.current = options?.onError;\n\n const enroll = useCallback(async () => {\n if (initializingRef.current || !enabled) {\n return;\n }\n\n initializingRef.current = true;\n setLoading(true);\n setError(undefined);\n\n try {\n const enrollResult = (await medplum.executeBot(\n DOSESPOT_SELF_ENROLL_PRESCRIBER_BOT,\n {}\n )) as DoseSpotSelfEnrollmentResult;\n\n setResult(enrollResult);\n onSuccessRef.current?.(enrollResult);\n } catch (err: unknown) {\n setError(err);\n onErrorRef.current?.(err);\n } finally {\n setLoading(false);\n }\n }, [medplum, enabled]);\n\n useEffect(() => {\n enroll().catch(console.error);\n }, [enroll]);\n\n return { result, loading, error };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["directions", "useMedplum", "useState", "useMedplum", "useState", "useMedplum", "useCallback", "useEffect", "useRef", "useState", "useMedplum", "useRef", "useState", "useCallback", "useEffect", "useMedplum", "useCallback", "useState", "addPreferredPharmacyToPatient", "createPreferredPharmacyExtension", "getPreferredPharmaciesFromPatient", "isAddPharmacyResponse", "isOrganizationArray", "PATIENT_PREFERRED_PHARMACY_URL", "PHARMACY_PREFERENCE_TYPE_SYSTEM", "PHARMACY_TYPE_PREFERRED", "PHARMACY_TYPE_PRIMARY", "removePreferredPharmacyFromPatient", "useState", "useMedplum", "useCallback", "useMedplum", "useCallback", "useEffect", "useRef", "useState", "useMedplum", "useRef", "useState", "useCallback", "useEffect"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@medplum/dosespot-react",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.10",
|
|
4
4
|
"description": "Medplum DoseSpot React SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"medplum",
|
|
@@ -60,11 +60,11 @@
|
|
|
60
60
|
"test:watch": "vitest watch"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@medplum/core": "5.1.
|
|
64
|
-
"@medplum/dosespot-core": "5.1.
|
|
65
|
-
"@medplum/fhirtypes": "5.1.
|
|
66
|
-
"@medplum/mock": "5.1.
|
|
67
|
-
"@medplum/react-hooks": "5.1.
|
|
63
|
+
"@medplum/core": "5.1.10",
|
|
64
|
+
"@medplum/dosespot-core": "5.1.10",
|
|
65
|
+
"@medplum/fhirtypes": "5.1.10",
|
|
66
|
+
"@medplum/mock": "5.1.10",
|
|
67
|
+
"@medplum/react-hooks": "5.1.10",
|
|
68
68
|
"@testing-library/jest-dom": "6.9.1",
|
|
69
69
|
"@testing-library/react": "16.3.2",
|
|
70
70
|
"@types/node": "22.19.17",
|
|
@@ -74,13 +74,13 @@
|
|
|
74
74
|
"jsdom": "29.0.2",
|
|
75
75
|
"react": "19.2.5",
|
|
76
76
|
"react-dom": "19.2.5",
|
|
77
|
-
"vitest": "4.1.
|
|
77
|
+
"vitest": "4.1.5"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"@medplum/core": "5.1.
|
|
81
|
-
"@medplum/dosespot-core": "5.1.
|
|
82
|
-
"@medplum/fhirtypes": "5.1.
|
|
83
|
-
"@medplum/react-hooks": "5.1.
|
|
80
|
+
"@medplum/core": "5.1.10",
|
|
81
|
+
"@medplum/dosespot-core": "5.1.10",
|
|
82
|
+
"@medplum/fhirtypes": "5.1.10",
|
|
83
|
+
"@medplum/react-hooks": "5.1.10",
|
|
84
84
|
"react": "^18.0.0 || ^19.0.0"
|
|
85
85
|
},
|
|
86
86
|
"engines": {
|