feat(dashboard, api): integrate BMI thresholds and enhance dashboard with health metrics
This commit is contained in:
7
lib/types/bmi.ts
Normal file
7
lib/types/bmi.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum BmiCategory {
|
||||
UNDER_WEIGHT = 'under_weight',
|
||||
NORMAL = 'normal',
|
||||
OVER_WEIGHT = 'over_weight',
|
||||
VERY_OVERWEIGHT = 'very_overweight',
|
||||
OBESE = 'obese',
|
||||
}
|
||||
59
lib/utils.ts
59
lib/utils.ts
@@ -1,6 +1,9 @@
|
||||
import { Database } from '@/packages/supabase/src/database.types';
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import { BmiCategory } from './types/bmi';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
@@ -19,11 +22,63 @@ export function toTitleCase(str?: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export function sortByDate<T>(a: T[] | undefined, key: keyof T): T[] | undefined {
|
||||
export function sortByDate<T>(
|
||||
a: T[] | undefined,
|
||||
key: keyof T,
|
||||
): T[] | undefined {
|
||||
return a?.sort((a, b) => {
|
||||
if (!a[key] || !b[key]) {
|
||||
return 0;
|
||||
}
|
||||
return new Date(b[key] as string).getTime() - new Date(a[key] as string).getTime();
|
||||
return (
|
||||
new Date(b[key] as string).getTime() -
|
||||
new Date(a[key] as string).getTime()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export const bmiFromMetric = (kg: number, cm: number) => {
|
||||
const m = cm / 100;
|
||||
return kg / (m * m);
|
||||
};
|
||||
|
||||
export function getBmiStatus(
|
||||
thresholds: Omit<
|
||||
Database['medreport']['Tables']['bmi_thresholds']['Row'],
|
||||
'id'
|
||||
>[],
|
||||
params: { age: number; height: number; weight: number },
|
||||
): BmiCategory | null {
|
||||
const age = params.age;
|
||||
const thresholdByAge =
|
||||
thresholds.find(
|
||||
(b) => age >= b.age_min && (b.age_max == null || age <= b.age_max),
|
||||
) || null;
|
||||
const bmi = bmiFromMetric(params.weight, params.height);
|
||||
|
||||
if (!thresholdByAge || Number.isNaN(bmi)) return null;
|
||||
|
||||
if (bmi > thresholdByAge.obesity_min) return BmiCategory.OBESE;
|
||||
if (bmi > thresholdByAge.strong_min) return BmiCategory.VERY_OVERWEIGHT;
|
||||
if (bmi > thresholdByAge.overweight_min) return BmiCategory.OVER_WEIGHT;
|
||||
if (bmi >= thresholdByAge.normal_min && bmi <= thresholdByAge.normal_max)
|
||||
return BmiCategory.NORMAL;
|
||||
return BmiCategory.UNDER_WEIGHT;
|
||||
}
|
||||
|
||||
export function getBmiBackgroundColor(bmiStatus: BmiCategory | null): string {
|
||||
switch (bmiStatus) {
|
||||
case BmiCategory.UNDER_WEIGHT:
|
||||
return 'bg-warning';
|
||||
case BmiCategory.NORMAL:
|
||||
return 'bg-success';
|
||||
case BmiCategory.OVER_WEIGHT:
|
||||
return 'bg-warning';
|
||||
case BmiCategory.VERY_OVERWEIGHT:
|
||||
return 'bg-destructive';
|
||||
case BmiCategory.OBESE:
|
||||
return 'bg-error';
|
||||
default:
|
||||
return 'bg-success';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user