Adding drug dose calculator (Atorvastatin), GRACE(Discharge) score

This commit is contained in:
2026-04-06 08:59:10 +05:30
parent 4c36ad570c
commit dfe1f52545
9 changed files with 505 additions and 41 deletions

View File

@@ -7,6 +7,10 @@ export default function InputComponent({id, type, inputOptions, min, max, requir
const [error, setError] = useState<string>(); const [error, setError] = useState<string>();
const [option, setOption] = useState<string>(""); const [option, setOption] = useState<string>("");
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [search, setSearch] = useState<string>("");
const filteredOptions = inputOptions?.filter(e =>
e.label.toLowerCase().includes(search.toLowerCase())
);
if(type === "number"){ if(type === "number"){
return( return(
<> <>
@@ -127,51 +131,56 @@ export default function InputComponent({id, type, inputOptions, min, max, requir
) )
}else if(type === "search"){ }else if(type === "search"){
console.log('ran'); console.log('ran');
return( return (
<>
<fieldset className="fieldset"> <fieldset className="fieldset">
<legend className="fieldset-legend text-black"> { info ? <legend className="fieldset-legend text-black">
<> {name} {defaultUnit ? `(${defaultUnit})` : null}
<div className="tooltip inline-block" data-tip={info}> </legend>
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" <div className="relative">
className="w-4 h-4" viewBox="0 0 416.979 416.979" <input
> type="text"
<g> className="input bg-white text-black border-black"
<path d="M356.004,61.156c-81.37-81.47-213.377-81.551-294.848-0.182c-81.47,81.371-81.552,213.379-0.181,294.85 placeholder={search !== "" ? search : placeholder}
c81.369,81.47,213.378,81.551,294.849,0.181C437.293,274.636,437.375,142.626,356.004,61.156z M237.6,340.786 value={search}
c0,3.217-2.607,5.822-5.822,5.822h-46.576c-3.215,0-5.822-2.605-5.822-5.822V167.885c0-3.217,2.607-5.822,5.822-5.822h46.576
c3.215,0,5.822,2.604,5.822,5.822V340.786z M208.49,137.901c-18.618,0-33.766-15.146-33.766-33.765
c0-18.617,15.147-33.766,33.766-33.766c18.619,0,33.766,15.148,33.766,33.766C242.256,122.755,227.107,137.901,208.49,137.901z"/>
</g>
</svg>
</div></> : null
} {name} {defaultUnit ? `(${defaultUnit})` : null} </legend>
<input type="text"
className="input bg-white text-black border-black"
placeholder={placeholder}
list="searchlist"
aria-required={required}
onChange={(e) => { onChange={(e) => {
setOption(e.target.value); setSearch(e.target.value);
if(e.target.value !== ""){ setOpen(true);
setOpen(true); if(e.target.value === ""){
}else{ return;
setOpen(false);
} }
handleDataChange !== undefined ? handleDataChange(id, e.target.value) : null; handleDataChange !== undefined ? handleDataChange(id, e.target.value) : null;
}} }}
required={required} onFocus={() => setOpen(true)}
/> />
<datalist id="searchlist"> {open && (
{open && inputOptions?.map(e => { <div className="absolute left-0 top-full mt-0 z-50 w-full bg-white border border-black max-h-48 overflow-y-auto rounded shadow">
return( {filteredOptions?.length ? (
<option key={e.label} value={e.value}/> filteredOptions.map((e) => (
) <div
})} key={e.value}
</datalist> className="px-3 py-2 hover:bg-gray-200 cursor-pointer"
{/* <span className="label text-red-400">Optional</span> */} onClick={() => {
setSearch(e.label);
setOpen(false);
}}
onMouseDown={() => {
setSearch(e.label);
setOpen(false);
handleDataChange !== undefined ? handleDataChange(id, e.value) : null;
}}
>
{e.label}
</div>
))
) : (
<div className="px-3 py-2 text-gray-500">No results</div>
)}
</div>
)}
</div>
</fieldset> </fieldset>
</>
); );
} }
} }

View File

@@ -32,7 +32,7 @@ export default function Result({interpretation, calculated_value, unit, id, sect
<path d="M8.37032 11.0726L5.41421 14.0287C4.63317 14.8097 4.63316 16.076 5.41421 16.8571L6.95611 18.399C7.73715 19.18 9.00348 19.18 9.78453 18.399L12.7406 15.4429M11.0726 8.37032L14.0287 5.41421C14.8097 4.63317 16.076 4.63316 16.8571 5.41421L18.399 6.95611C19.18 7.73715 19.18 9.00348 18.399 9.78453L15.4429 12.7406M6.64883 6.64883L4.88296 4.88296M19.0992 19.0992L17.3333 17.3333M9.35119 5.87299V4M14.6488 20V18.127M5.87299 9.35119H4M20 14.6488H18.127" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> <path d="M8.37032 11.0726L5.41421 14.0287C4.63317 14.8097 4.63316 16.076 5.41421 16.8571L6.95611 18.399C7.73715 19.18 9.00348 19.18 9.78453 18.399L12.7406 15.4429M11.0726 8.37032L14.0287 5.41421C14.8097 4.63317 16.076 4.63316 16.8571 5.41421L18.399 6.95611C19.18 7.73715 19.18 9.00348 18.399 9.78453L15.4429 12.7406M6.64883 6.64883L4.88296 4.88296M19.0992 19.0992L17.3333 17.3333M9.35119 5.87299V4M14.6488 20V18.127M5.87299 9.35119H4M20 14.6488H18.127" stroke="#000000" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg> </svg>
<h6 className="leading-none">{calc.name}</h6></Link> : null} <h6 className="leading-none">{calc.name}</h6></Link> : null}
<h2 className="card-title self-center">Calculated Score/Value: {calculated_value} {unit}</h2> <h2 className="card-title self-center">Calculated Score/Value: {String(calculated_value)} {unit}</h2>
<p>{message}</p> <p>{message}</p>
{diagnosis ? <p>Diagnosis: {diagnosis}</p> : null} {diagnosis ? <p>Diagnosis: {diagnosis}</p> : null}
{advice ? <p>Advice: {advice}</p> : null} {advice ? <p>Advice: {advice}</p> : null}

View File

@@ -0,0 +1,146 @@
import { Calculator, Drug, Input, Interpretation, Values, inputOptions } from "../types"
import { DrugRegistry } from "./drugs/drug_registry"
function generateDrugList():inputOptions[] {
const drugs:inputOptions[] = DrugRegistry.map(e => {
console.log(e.name);
const drug:inputOptions = {
label:e.name,
value:e.id
}
return drug;
});
console.log(drugs);
return drugs;
}
const parameters:Input[] = [
{
id:"drug",
name:"Drug name",
placeholder:"Select the drug by searching its name below",
type:"search",
inputOptions:generateDrugList()
},
{
id:"weight",
name:"Weight",
placeholder:"Enter the weight of the patient",
type:"number"
},
{
id:"ckd",
name:"CKD Stage",
placeholder:"Whether the patient has CKD, if yes, select the stage",
type:"select",
inputOptions:[
{
label:"None",
value:"none"
},
{
label:"CKD stage 3a",
value:"3A"
},
{
label:"CKD Stage 3B",
value:"3B"
},
{
label:"CKD Stage 4",
value:"4"
},
{
label:"CKD Stage 5 / ESRD",
value:"5"
}
]
},
{
id:"cld",
name:"CLD Stage (Child Pugh)",
placeholder:"Whether the patient has CLD, if yes, select the Child Pugh Stage",
type:"select",
inputOptions:[
{
label:"None",
value:"none"
},
{
label:"Child Pugh A",
value:"A"
},
{
label:"Child Pugh B",
value:"B"
},
{
label:"Child Pugh C",
value:"C"
}
]
}
]
export const Adult_Drug_Calculator:Calculator = {
id:"adult_drug_calculator",
name:"Adult Dose Calculator",
desc:"Adult Dose Calculator can be used to calculate the drug dosing of different dose which also accounts for different comorbidities like CKD and CLD",
inputs:parameters,
calc_func:(values : Values) : string | number => {
const drugID = values.drug as string;
const weight = values.weight as number;
const ckd = values.ckd as "none" | "3A" | "3B" | "4" | "5";
const cld = values.cld as "none" | "A" | "B" | "C";
const drug = DrugRegistry.find(e => e.id === drugID);
if(drug){
if(typeof drug.cld_dosing !== "undefined" && typeof drug.ckd_dosing !== "undefined" && ckd !== "none" && cld == "none"){
const dosingObj = drug.cld_dosing.find(e => e.stage === cld);
if(drug.isWeightBased){
return `${Math.floor(dosingObj?.dosing * weight)} ${drug.unit}`;
}else{
return `${Math.floor(dosingObj?.dosing)} ${drug.unit}` || 0;
}
}else if(typeof drug.cld_dosing !== "undefined" && cld !== "none"){
const dosingObj = drug.cld_dosing.find(e => e.stage === cld);
if(drug.isWeightBased){
return `${Math.floor(dosingObj?.dosing * weight)} ${drug.unit}`;
}else{
return `${Math.floor(dosingObj?.dosing)} ${drug.unit}` || 0;
}
}else if(typeof drug.ckd_dosing !== "undefined" && ckd !== "none"){
const dosingObj = drug.ckd_dosing.find(e => e.stage === ckd);
if(drug.isWeightBased){
return `${Math.floor(dosingObj?.dosing * weight)} ${drug.unit}`;
}else{
return `${Math.floor(dosingObj?.dosing)} ${drug.unit}` || 0;
}
}else{
if(drug.isWeightBased){
return `${Math.floor(drug.adult_dosing * weight)} ${drug.unit}`;
}else{
return `${drug.adult_dosing} ${drug.unit}`;
}
}
}else{
return -1;
}
},
interpret_func:(dosing:number | string):Interpretation => {
if(dosing === 0){
return {
level:"severe",
message:"Drug is to be avoided in patient"
}
}else if(dosing === -1){
return {
level:"none",
message:"Drug not found"
}
}else{
return {
level:"low",
message:`Calculated dosing for the drug is ${dosing ? dosing : null}`
}
}
}
}

View File

@@ -0,0 +1,24 @@
import { Drug } from "../../../types";
export const Atorvastatin:Drug = {
id:"atorvastatin",
name:"Atorvastatin",
adult_dosing:"10mg to 20mg",
pediatric_dosing:"10mg to 20mg",
isWeightBased:false,
max_dose:"80mg",
max_dose_pediatric:"20mg",
unit:"mg",
cld_dosing:[
{
stage:"B",
to_be_given:true,
dosing:"5-10mg OD",
max_dose:"20mg"
},
{
stage:"C",
to_be_given:false
}
]
};

View File

@@ -0,0 +1,23 @@
import { Drug } from "../../../types";
export const metoprolol:Drug = {
id:"metoprolol",
name:"Metoprolol",
adult_dosing:1.5,
max_dose:"200mg",
isWeightBased:true,
unit:"mg",
pediatric_dosing:0.5,
cld_dosing:[
{
stage:"B",
to_be_given:true,
dosing:0.5
},
{
stage:"C",
to_be_given:true,
dosing:0.25
}
]
}

View File

@@ -0,0 +1,8 @@
import { Drugs } from "../../types";
import { Atorvastatin } from "./Cardiology/atorvastatin";
import { metoprolol } from "./Cardiology/metoprolol";
export const DrugRegistry:Drugs = [
Atorvastatin,
metoprolol
];

View File

@@ -0,0 +1,216 @@
import { Calculator, Input, Interpretation, Values } from "../types";
const parameters:Input[] = [
{
id:"age",
name:"Age",
placeholder:"Enter the age",
type:"number",
required:true
},
{
id:"hr",
name:"Heart Rate",
placeholder:"Enter the Heart Rate",
type:"number",
required:true,
defaultUnit:"beats/minute"
},
{
id:"sbp",
name:"Systolic Blood Pressure",
placeholder:"Enter the Systolic Blood Pressure",
type:"number",
required:true,
defaultUnit:"mmHg"
},
{
id:"cret",
name:"Serum Creatinine",
placeholder:"Enter the serum creatinine",
type:"number",
required:true,
defaultUnit:"mg/dL"
},
{
id:"cardiacmarkers",
name:"Elevated Cardiac Markers",
placeholder:"Whether the patient had elevated cardiac markers",
type:"checkbox",
required:true
},
{
id:"deviation",
name:"ST segment depression",
placeholder:"Whether the patient had ST segment depression",
type:"checkbox",
required:true
},
{
id:"chf",
name:"Congestive Heart Failure",
placeholder:"Whether the patient has a history of Congestive Heart Failure",
type:"checkbox"
},
{
id:"pci",
name:"Percutaneous Coronary Intervention",
placeholder:"Whether the patient had a PCI during hospital stay",
type:"checkbox"
},
{
id:"mi",
name:"History of Myocardial Infarction",
placeholder:"Whether the patient has a history of Myocardial Infarction",
type:"checkbox"
}
]
export const GRACE_Discharge : Calculator = {
id:"grace_discharge",
name:"GRACE 1.0 (Discharge)",
desc:"The GRACE (Global Registry for Acut Coronary Events) Score is a clinical tool to estimate the risk of death or myocardial infarction (MI) for 6 weeks in patients after discharge who have suffered Acute Coronary Syndrone (ACS)",
inputs:parameters,
calc_func:(values: Values):number => {
const age = values.age as number;
const hr = values.hr as number;
const sbp = values.sbp as number;
const cret = values.cret as number;
const chf = values.chf as boolean;
const pci = values.pci as boolean;
const deviation = values.deviation as boolean;
const mi = values.mi as boolean;
const cardiacmarkers = values.cardiacmarkers as boolean;
var score = 0;
if(age < 40) score = score + 0;
else if(age >=40 && age < 50) score = score + 18;
else if(age >= 50 && age < 60) score = score + 36;
else if(age >= 60 && age < 70) score = score + 55;
else if(age >= 70 && age < 80) score = score + 73
else if(age >= 80 && age < 90) score = score + 91;
else score = score + 100;
if (hr < 50) score += 0;
else if (hr <= 69) score += 3;
else if (hr <= 89) score += 9;
else if (hr <= 109) score += 14;
else if (hr <= 149) score += 23;
else if (hr <= 199) score += 35;
else score += 43;
if (sbp < 80) score += 24;
else if (sbp <= 99) score += 22;
else if (sbp <= 119) score += 18;
else if (sbp <= 139) score += 14;
else if (sbp <= 159) score += 10;
else if (sbp <= 199) score += 4;
else score += 0;
if (cret <= 0.39) score += 1;
else if (cret <= 0.79) score += 3;
else if (cret <= 1.19) score += 5;
else if (cret <= 1.59) score += 7;
else if (cret <= 1.99) score += 9;
else if (cret <= 3.99) score += 15;
else score += 20;
if (chf) score += 24;
if (deviation) score += 11;
if(pci) score += 14;
if(mi) score += 12;
if(cardiacmarkers) score += 15;
return score;
},
interpret_func:(score : number) : Interpretation => {
if(score <= 60) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 0.2%"
}
if(score <= 70 ) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 0.3%"
}
if(score <= 80 ) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 0.4%"
}
if(score <= 90 ) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 0.6%"
}
if(score <= 100 ) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 0.8%"
}
if(score <= 110 ) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 1.1%"
}
if(score <= 120 ) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 1.6%"
}
if(score <= 130 ) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 2.1%"
}
if(score <= 140 ) return {
level:"low",
message:"Probability of In Hospital Mortality is less than or equal to 2.9%"
}
if(score <= 150 ) return {
level:"moderate",
message:"Probability of In Hospital Mortality is less than or equal to 3.9%"
}
if(score <= 160 ) return {
level:"moderate",
message:"Probability of In Hospital Mortality is less than or equal to 5.4%"
}
if(score <= 170 ) return {
level:"moderate",
message:"Probability of In Hospital Mortality is less than or equal to 7.3%"
}
if(score <= 180 ) return {
level:"severe",
message:"Probability of In Hospital Mortality is less than or equal to 9.8%"
}
if(score <= 190 ) return {
level:"severe",
message:"Probability of In Hospital Mortality is less than or equal to 13%"
}
if(score <= 200 ) return {
level:"severe",
message:"Probability of In Hospital Mortality is less than or equal to 18%"
}
if(score <= 210 ) return {
level:"severe",
message:"Probability of In Hospital Mortality is less than or equal to 23%"
}
if(score <= 220 ) return {
level:"severe",
message:"Probability of In Hospital Mortality is less than or equal to 29%"
}
if(score <= 230 ) return {
level:"severe",
message:"Probability of In Hospital Mortality is less than or equal to 36%"
}
if(score <= 240 ) return {
level:"severe",
message:"Probability of In Hospital Mortality is less than or equal to 44%"
}
if(score < 250 ) return {
level:"severe",
message:"Probability of In Hospital Mortality is less than or equal to 44%"
}
if(score >= 250) return {
level:"severe",
message:"Probability of In Hospital Mortality is more than or equal to 52%"
}
return{
level:"none",
message:"Invalid Value"
}
}
}

View File

@@ -9,6 +9,8 @@ import { MELDNa } from "./Utilities/MELDNa";
import { GRACE_Inhosp } from "./Emergency/GRACE_Inhosp"; import { GRACE_Inhosp } from "./Emergency/GRACE_Inhosp";
import { HEART_Score } from "./General Cardiology/HEART_Score"; import { HEART_Score } from "./General Cardiology/HEART_Score";
import { HASBLED } from "./Arrhythmias and Anti-Coagulation/HAS-BLED"; import { HASBLED } from "./Arrhythmias and Anti-Coagulation/HAS-BLED";
import { GRACE_Discharge } from "./General Cardiology/GRACE_Discharge";
import { Adult_Drug_Calculator } from "./Drug Dose Calculator/Adult";
export const CalculatorRegistry : Record<string, Section> = { export const CalculatorRegistry : Record<string, Section> = {
generalCardiology:{ generalCardiology:{
@@ -17,7 +19,17 @@ export const CalculatorRegistry : Record<string, Section> = {
textColor:"#ccc", textColor:"#ccc",
svg:"", svg:"",
calculators:[ calculators:[
MAP MAP,
GRACE_Discharge
]
},
drugDoseCalculator:{
id:"drugDosing",
displayName:"Drug Dose Calculator",
textColor:"#ccc",
svg:"",
calculators:[
Adult_Drug_Calculator
] ]
}, },
arrhythmias:{ arrhythmias:{

View File

@@ -8,7 +8,7 @@ export interface Calculator {
interpret_func:Function, interpret_func:Function,
customComponent?:React.FC customComponent?:React.FC
} }
interface inputOptions { export interface inputOptions {
label:string, label:string,
value:string | number ; value:string | number ;
} }
@@ -52,3 +52,29 @@ export interface pastUsed {
unit: string | undefined unit: string | undefined
} }
export type Values = Record<string, string | number | boolean >; export type Values = Record<string, string | number | boolean >;
export interface Drug {
id:string,
name:string,
adult_dosing:number | string,
pediatric_dosing?:number | string,
max_dose: number | string,
max_dose_pediatric?: number | string
isWeightBased:boolean
unit:string,
ckd_dosing?:
{
stage: "none" |"3A" | "3B" | "4" | "ESRD",
to_be_given:boolean
dosing?:number | string,
max_dose?:number | string
}[],
cld_dosing?: {
stage:"none" | "A" | "B" | "C",
to_be_given: boolean,
dosing?: number | string,
max_dose?:number | string
}[]
}
export type Drugs = Drug[];