Skip to content

Commit 657f9a7

Browse files
authored
Merge branch 'main' into main
2 parents dab8b29 + 3c30ee2 commit 657f9a7

16 files changed

Lines changed: 619 additions & 9 deletions

File tree

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,6 @@
157157
"react-snap": "^1.23.0",
158158
"tailwind-scrollbar": "^2.1.0",
159159
"tailwindcss": "^3.4.1",
160-
"typescript": "^5.3.3"
160+
"typescript": "^5.9.3"
161161
}
162162
}

‎src/common/Testimonial/TestimonialCard.jsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ const TestimonialCard = ({ home, quote, name, avatarUrl, category, created_at, e
5353
</div>
5454

5555
<div className="mx-2 mt-4">
56-
<blockquote className={`${home && 'h-32'} max-h-32 px-6 overflow-y-auto`}>
56+
<blockquote
57+
className={`${home ? 'h-32' : ''} max-h-32 px-6 overflow-y-auto`}
58+
style={{ touchAction: 'pan-y' }}
59+
>
5760
<p
5861
className="leading-relaxed text-gray-700"
5962
dangerouslySetInnerHTML={{ __html: replaceWithBr() }}

‎src/common/Testimonial/TestimonialSection.jsx‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@ function TestimonialSection() {
1818
const [testimonials, setTestimonials] = useState([]);
1919

2020
const fetchTestimonials = async () => {
21-
const res = await submit(fetchTestimonialsHomePage());
22-
setTestimonials(res);
21+
try {
22+
const res = await submit(fetchTestimonialsHomePage());
23+
setTestimonials(res || []);
24+
} catch (error) {
25+
console.warn('Failed to fetch testimonials:', error.message);
26+
setTestimonials([]);
27+
}
2328
};
2429

2530
useEffect(() => {
@@ -30,6 +35,7 @@ function TestimonialSection() {
3035
<>
3136
<div className="mx-5 lg:mx-20 h-72 mt-16 sm:mt-20">
3237
<Swiper
38+
grabCursor
3339
rewind
3440
autoplay={{
3541
delay: 2500,
@@ -56,6 +62,7 @@ function TestimonialSection() {
5662
}}
5763
slidesPerView={1}
5864
spaceBetween={10}
65+
touchEventsTarget="container"
5966
>
6067
{testimonials &&
6168
testimonials.map((testimonial) => (

‎src/common/Testimonial/Testimonials.jsx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@ const Testimonials = () => {
1515
const isAuthenticated = useAuthenticated();
1616

1717
const fetchTestimonials = async () => {
18-
const res = await submit(fetchAllTestimonials());
19-
setTestimonials(res);
18+
try {
19+
const res = await submit(fetchAllTestimonials());
20+
setTestimonials(res || []);
21+
} catch (error) {
22+
console.warn('Failed to fetch testimonials:', error.message);
23+
setTestimonials([]);
24+
}
2025
};
2126

2227
const handleLogin = (value) => {

‎src/common/header/HeaderNav.jsx‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from 'react';
1+
import { useState, useEffect, useRef } from 'react';
22
import { Link } from 'react-router-dom';
33
import { BsGithub, BsTrophyFill } from 'react-icons/bs';
44
import { FaLightbulb } from 'react-icons/fa';
@@ -16,6 +16,26 @@ const HeaderNav = ({ showBrowse }) => {
1616
const { showShareModal, setShowShareModal } = useSearchContext();
1717

1818
const [showToggleMenu, setShowToggleMenu] = useState(false);
19+
const menuRef = useRef(null);
20+
21+
// Close drawer when clicking outside the menu panel
22+
useEffect(() => {
23+
if (!showToggleMenu) return;
24+
25+
const handleClickOutside = (event) => {
26+
if (menuRef.current && !menuRef.current.contains(event.target)) {
27+
setShowToggleMenu(false);
28+
}
29+
};
30+
31+
document.addEventListener('mousedown', handleClickOutside);
32+
document.addEventListener('touchstart', handleClickOutside);
33+
34+
return () => {
35+
document.removeEventListener('mousedown', handleClickOutside);
36+
document.removeEventListener('touchstart', handleClickOutside);
37+
};
38+
}, [showToggleMenu]);
1939

2040
const [anchorEl, setAnchorEl] = useState(null);
2141

@@ -129,6 +149,7 @@ const HeaderNav = ({ showBrowse }) => {
129149
<ul
130150
className="header-links"
131151
data-testid="header-links-container"
152+
ref={menuRef}
132153
onClick={(e) => e.stopPropagation()}
133154
>
134155
<li className="menu-closer">

‎src/common/header/header.css‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@
2929
}
3030

3131
.nav--visible {
32-
transform: translateY(0);
32+
transform: none;
33+
}
34+
35+
/* Push page content below the fixed header, but not the footer */
36+
.nav-wrapper ~ *:not(footer) {
37+
padding-top: 64px;
3338
}
3439

3540

‎src/common/home/home.css‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
width: 100%;
1414
overflow-x: hidden;
1515
min-height: 100vh;
16+
padding-top: 32px; /* extra offset for activity banner on home page */
1617
}
1718

1819
.app-home-body .app-home-body-content {

‎src/common/playleaderboard/LeaderBoard.jsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import TopPlayCreators from './TopPlayCreators';
66
import { Watch } from 'react-loader-spinner';
77
import { groupBy } from 'lodash';
88
import { format, lastDayOfMonth } from 'date-fns';
9+
import './leaderBoard.css';
910

1011
const LeaderBoard = () => {
1112
const [top10Contributors, updateTop10Contributors] = useState([]);
@@ -69,7 +70,7 @@ const LeaderBoard = () => {
6970
}, [publishedPlays]);
7071

7172
return (
72-
<main className="app-body app-body-overflow-hidden">
73+
<main className="app-body">
7374
{publishedPlays.length && (topContributorOfTheMonth || top10Contributors) ? (
7475
<div className=" overflow-auto lg:flex flex-row justify-center">
7576
{topContributorOfTheMonth && (

‎src/common/playleaderboard/leaderBoard.css‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@
3838
height: 100vh;
3939
} */
4040

41+
.leaderboard-loader {
42+
display: flex;
43+
align-items: center;
44+
justify-content: center;
45+
min-height: calc(100vh - 160px);
46+
}
47+
4148
.leaderboard-wrapper {
4249
background-color: #ffffff;
4350
border-radius: 16px;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React, { useMemo, useState } from 'react';
2+
3+
import './styles/styles.css';
4+
import CalculatorForm from './components/CalculatorForm';
5+
import ResultDisplay from './components/ResultDisplay';
6+
7+
import { calculateBMR, calculateTDEE } from './utils/healthUtils';
8+
import { FormData } from './types';
9+
10+
const BodyCompositionCalculator: React.FC = () => {
11+
const [formData, setFormData] = useState<FormData>({
12+
gender: 'male',
13+
weight: '',
14+
height: '',
15+
age: '',
16+
activityLevel: 'sedentary'
17+
});
18+
19+
/**
20+
* Generic handler for updating form state
21+
*/
22+
const handleChange = (field: keyof FormData, value: string) => {
23+
setFormData((prev) => ({
24+
...prev,
25+
[field]: value
26+
}));
27+
};
28+
29+
/**
30+
* Convert Inputs Safely
31+
*/
32+
const numericWeight = Number(formData.weight);
33+
const numericHeight = Number(formData.height);
34+
const numericAge = Number(formData.age);
35+
36+
const isValid = numericWeight > 0 && numericHeight > 0 && numericAge > 0;
37+
38+
/**
39+
* Derived BMR
40+
*/
41+
const bmr = useMemo(() => {
42+
if (!isValid) return null;
43+
44+
return calculateBMR({
45+
gender: formData.gender,
46+
weight: numericWeight,
47+
height: numericHeight,
48+
age: numericAge
49+
});
50+
}, [formData.gender, numericWeight, numericHeight, numericAge, isValid]);
51+
52+
/**
53+
* Derived TDEE
54+
*/
55+
const tdee = useMemo(() => {
56+
if (!bmr) return null;
57+
58+
return calculateTDEE(bmr, formData.activityLevel);
59+
}, [bmr, formData.activityLevel]);
60+
61+
return (
62+
<div className="container">
63+
<h2 className="calculator-title">BMR & TDEE Calculator</h2>
64+
65+
<p className="calculator-summary">
66+
<strong>BMR (Basal Metabolic Rate)</strong> is the number of calories your body needs at
67+
complete rest to maintain essential functions such as breathing, circulation, and
68+
temperature regulation.
69+
<br />
70+
<br />
71+
<strong>TDEE (Total Daily Energy Expenditure)</strong> is the total number of calories your
72+
body burns in a day, including all physical activities like walking, exercise, and daily
73+
tasks.
74+
</p>
75+
<CalculatorForm formData={formData} onChange={handleChange} />
76+
77+
<ResultDisplay bmr={bmr} tdee={tdee} />
78+
</div>
79+
);
80+
};
81+
82+
export default BodyCompositionCalculator;

0 commit comments

Comments
 (0)