Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useMemo, useState } from 'react';

import './styles/styles.css';
import CalculatorForm from './components/CalculatorForm';
import ResultDisplay from './components/ResultDisplay';

import { calculateBMR, calculateTDEE } from './utils/healthUtils';
import { FormData } from './types';

const BodyCompositionCalculator: React.FC = () => {
const [formData, setFormData] = useState<FormData>({
gender: 'male',
weight: '',
height: '',
age: '',
activityLevel: 'sedentary'
});

/**
* Generic handler for updating form state
*/
const handleChange = (field: keyof FormData, value: string) => {
setFormData((prev) => ({
...prev,
[field]: value
}));
};

/**
* Convert Inputs Safely
*/
const numericWeight = Number(formData.weight);
const numericHeight = Number(formData.height);
const numericAge = Number(formData.age);

const isValid = numericWeight > 0 && numericHeight > 0 && numericAge > 0;

/**
* Derived BMR
*/
const bmr = useMemo(() => {
if (!isValid) return null;

return calculateBMR({
gender: formData.gender,
weight: numericWeight,
height: numericHeight,
age: numericAge
});
}, [formData.gender, numericWeight, numericHeight, numericAge, isValid]);

/**
* Derived TDEE
*/
const tdee = useMemo(() => {
if (!bmr) return null;

return calculateTDEE(bmr, formData.activityLevel);
}, [bmr, formData.activityLevel]);

return (
<div className="container">
<h2 className="calculator-title">BMR & TDEE Calculator</h2>

<p className="calculator-summary">
<strong>BMR (Basal Metabolic Rate)</strong> is the number of calories your body needs at
complete rest to maintain essential functions such as breathing, circulation, and
temperature regulation.
<br />
<br />
<strong>TDEE (Total Daily Energy Expenditure)</strong> is the total number of calories your
body burns in a day, including all physical activities like walking, exercise, and daily
tasks.
</p>
<CalculatorForm formData={formData} onChange={handleChange} />

<ResultDisplay bmr={bmr} tdee={tdee} />
</div>
);
};

export default BodyCompositionCalculator;
104 changes: 104 additions & 0 deletions src/plays/body-composition-calculator/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Body Composition Calculator

An interactive Body Composition Calculator that computes Basal Metabolic Rate (BMR) and Total Daily Energy Expenditure (TDEE) using scientifically validated formulas.

This calculator helps users understand their daily calorie requirements based on body measurements and activity level.

---

## Play Demographic

- Language: TypeScript
- Level: Intermediate
- Category: Health / Fitness / Calculator

---

## Creator Information

- User: deansereigns
- GitHub Link: https://github.com/deansereigns

---

## Implementation Details

This play is built using React and TypeScript with a modular and scalable architecture.

The calculator uses the **Mifflin-St Jeor Equation**, which is considered one of the most accurate formulas for estimating Basal Metabolic Rate.

### BMR (Basal Metabolic Rate)

BMR represents the number of calories your body requires to perform essential life-sustaining functions while at rest.

These include:

- Breathing
- Blood circulation
- Brain activity
- Cell production
- Maintaining body temperature

Formula used:

Male:

```text
BMR = (10 × weight_kg) + (6.25 × height_cm) − (5 × age_years) + 5
```

Female:

```text
BMR = (10 × weight_kg) + (6.25 × height_cm) − (5 × age_years) − 161
```

---

### TDEE (Total Daily Energy Expenditure)

TDEE represents the total calories your body burns in a day including all physical activities.

It is calculated by multiplying BMR with an activity multiplier.

Formula:
```text
TDEE = BMR × activity_multiplier
```

Activity multipliers used:

| Activity Level | Multiplier |
|---------------|------------|
| Sedentary | 1.2 |
| Light Activity | 1.375 |
| Moderate Activity | 1.55 |
| Active | 1.725 |
| Very Active | 1.9 |

---

### Features Implemented

- Built with TypeScript for type safety
- Modular and reusable component structure
- Real-time BMR and TDEE calculation using React Hooks
- Controlled form inputs with validation
- Clean and responsive user interface

---

## Considerations

- Validates inputs before performing calculations
- Handles empty or invalid values safely
- Optimized using React Hooks
- Easily extendable for additional health metrics

---

## Future Improvements

- BMI and body fat calculator
- Calorie recommendations for weight goals
- Enhanced UI and data visualization
104 changes: 104 additions & 0 deletions src/plays/body-composition-calculator/components/CalculatorForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { ChangeEvent } from 'react';
import { FormData, Gender, ActivityLevel } from '../types';

interface CalculatorFormProps {
formData: FormData;
onChange: (field: keyof FormData, value: string) => void;
}

/**
* Calculator Form
*
* Responsible for rendering and managing all input fields.
* This is a presentational component with no business logic.
*/
const CalculatorForm: React.FC<CalculatorFormProps> = ({ formData, onChange }) => {
/**
* Handle select change safely with typing
*/
const handleGenderChange = (event: ChangeEvent<HTMLSelectElement>) => {
onChange('gender', event.target.value as Gender);
};

const handleActivityChange = (event: ChangeEvent<HTMLSelectElement>) => {
onChange('activityLevel', event.target.value as ActivityLevel);
};

/**
* Handle numeric input changes
*/
const handleWeightChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange('weight', event.target.value);
};

const handleHeightChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange('height', event.target.value);
};

const handleAgeChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange('age', event.target.value);
};

return (
<div className="form">
{/* Gender*/}
<label>
Gender
<select value={formData.gender} onChange={handleGenderChange}>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</label>

{/* Weight*/}
<label>
Weight (kg)
<input
min="40"
placeholder="Enter weight"
type="number"
value={formData.weight}
onChange={handleWeightChange}
/>
</label>

{/* Height*/}
<label>
Height (cm)
<input
min="70"
placeholder="Enter Height"
type="number"
value={formData.height}
onChange={handleHeightChange}
/>
</label>

{/* Age*/}
<label>
Age
<input
min="10"
placeholder="Enter Age"
type="number"
value={formData.age}
onChange={handleAgeChange}
/>
</label>

{/* Activity Level */}
<label>
Activity Level
<select value={formData.activityLevel} onChange={handleActivityChange}>
<option value="sedentary">Sedentary (little or no exercise)</option>
<option value="light">Light (1-3 days/week)</option>
<option value="moderate">Moderate (3-5 days/week)</option>
<option value="active">Active (6-7 days/week)</option>
<option value="very_active">Very Active (physical job or intense training)</option>
</select>
</label>
</div>
);
};

export default CalculatorForm;
45 changes: 45 additions & 0 deletions src/plays/body-composition-calculator/components/ResultDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';

interface ResultDisplayProps {
bmr: number | null;
tdee: number | null;
}

/**
* ResultDisplay Component
*
* Displays calculated BMR and TDEE values.
* Pure Presentational component.
*/
const ResultDisplay: React.FC<ResultDisplayProps> = ({ bmr, tdee }) => {
/**
* Format number Safely
*/
const formatValue = (value: number | null): string => {
if (value === null || Number.isNaN(value)) {
return '--';
}

return `${Math.round(value)} kcal/day`;
};

return (
<div className="result-container">
<h3>Results</h3>

<div className="result-item">
<span className="label">BMR</span>
<span className="value">{formatValue(bmr)}</span>
</div>

<div className="result-item">
<span className="label">TDEE</span>
<span className="value">{formatValue(tdee)}</span>
</div>

{bmr === null && <p className="hint">Enter weight, height, and age to calculate.</p>}
</div>
);
};

export default ResultDisplay;
Loading
Loading