Skip to content

Commit 644ac84

Browse files
committed
modularize the quickmed home page
1 parent 91bb99c commit 644ac84

7 files changed

Lines changed: 393 additions & 309 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// client/src/components/quickmed/HomePage/FeaturesSection.jsx
2+
import React from 'react';
3+
import Icons from './Icons';
4+
5+
const FeaturesSection = () => {
6+
const features = [
7+
{
8+
icon: <Icons.Lightning />,
9+
title: 'Instant Search',
10+
description: 'Get real-time medicine suggestions as you type with our powerful autocomplete.',
11+
},
12+
{
13+
icon: <Icons.Shield />,
14+
title: 'FDA Verified Data',
15+
description: 'Access reliable drug information directly from the OpenFDA database.',
16+
},
17+
{
18+
icon: <Icons.Database />,
19+
title: 'Comprehensive Details',
20+
description: 'View usage, side effects, ingredients, warnings, and storage information.',
21+
},
22+
];
23+
24+
return (
25+
<section className="py-16 sm:py-24 bg-med-surface">
26+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
27+
<div className="text-center mb-12">
28+
<h3 className="text-2xl sm:text-3xl font-bold mb-4">Why Choose QuickMed?</h3>
29+
<p className="text-med-text-muted max-w-2xl mx-auto">
30+
Get accurate, comprehensive, and up-to-date medicine information at your fingertips.
31+
</p>
32+
</div>
33+
34+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
35+
{features.map((feature, index) => (
36+
<div
37+
key={index}
38+
className="group p-6 lg:p-8 bg-med-background rounded-2xl border border-med-border hover:border-med-green-500 transition-all duration-300 hover:shadow-xl hover:-translate-y-1"
39+
>
40+
<div className="w-16 h-16 mb-6 flex items-center justify-center rounded-xl bg-gradient-to-br from-med-green-100 to-med-blue-100 dark:from-med-green-900/30 dark:to-med-blue-600/30 text-med-green-600 dark:text-med-green-400 group-hover:scale-110 transition-transform duration-300">
41+
{feature.icon}
42+
</div>
43+
<h4 className="text-xl font-semibold mb-3">{feature.title}</h4>
44+
<p className="text-med-text-muted leading-relaxed">{feature.description}</p>
45+
</div>
46+
))}
47+
</div>
48+
</div>
49+
</section>
50+
);
51+
};
52+
53+
export default FeaturesSection;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// client/src/components/quickmed/HomePage/Footer.jsx
2+
import React from 'react';
3+
import Icons from './Icons';
4+
5+
const Footer = ({ navigateToQuickclinic }) => {
6+
return (
7+
<footer className="py-12 bg-med-surface border-t border-med-border">
8+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
9+
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
10+
{/* Brand Column */}
11+
<div className="text-center md:text-left">
12+
<div className="flex items-center justify-center md:justify-start gap-2 mb-3">
13+
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-med-green-500 to-med-green-600 flex items-center justify-center text-white">
14+
<Icons.Pill />
15+
</div>
16+
<span className="font-bold text-lg">QuickMed</span>
17+
</div>
18+
<p className="text-sm text-med-text-muted">Part of the QuickClinic ecosystem</p>
19+
</div>
20+
21+
{/* Disclaimer Column */}
22+
<div className="text-center">
23+
<h5 className="font-semibold mb-2 text-sm uppercase tracking-wide text-med-text-muted">
24+
Disclaimer
25+
</h5>
26+
<p className="text-sm text-med-text-muted leading-relaxed">
27+
Data sourced from OpenFDA. For educational purposes only and should not replace
28+
professional medical advice.
29+
</p>
30+
</div>
31+
32+
{/* CTA Column */}
33+
<div className="text-center md:text-right">
34+
<h5 className="font-semibold mb-2 text-sm uppercase tracking-wide text-med-text-muted">
35+
Need Medical Advice?
36+
</h5>
37+
<p className="text-sm text-med-text-muted mb-3">
38+
Book an appointment with top doctors near you.
39+
</p>
40+
<button
41+
onClick={navigateToQuickclinic}
42+
className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-r from-med-green-500 to-med-green-600 hover:from-med-green-600 hover:to-med-green-700 text-white font-medium rounded-lg transition-all duration-200 shadow-md hover:shadow-lg hover:-translate-y-0.5"
43+
>
44+
<span>Visit QuickClinic</span>
45+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
46+
<path
47+
strokeLinecap="round"
48+
strokeLinejoin="round"
49+
strokeWidth={2}
50+
d="M14 5l7 7m0 0l-7 7m7-7H3"
51+
/>
52+
</svg>
53+
</button>
54+
</div>
55+
</div>
56+
</div>
57+
</footer>
58+
);
59+
};
60+
61+
export default Footer;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// client/src/components/quickmed/HomePage/HeroSection.jsx
2+
import React from 'react';
3+
4+
const HeroSection = () => {
5+
return (
6+
<section className="relative overflow-hidden">
7+
{/* Decorative Elements */}
8+
<div className="absolute top-20 left-10 w-72 h-72 bg-med-green-400/20 rounded-full blur-3xl" />
9+
<div className="absolute bottom-20 right-10 w-96 h-96 bg-med-blue-400/20 rounded-full blur-3xl" />
10+
</section>
11+
);
12+
};
13+
14+
export default HeroSection;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// client/src/components/quickmed/HomePage/Icons.jsx
2+
import React from 'react';
3+
4+
const Icons = {
5+
Search: () => (
6+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
7+
<path
8+
strokeLinecap="round"
9+
strokeLinejoin="round"
10+
strokeWidth={2}
11+
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
12+
/>
13+
</svg>
14+
),
15+
Sun: () => (
16+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
17+
<path
18+
strokeLinecap="round"
19+
strokeLinejoin="round"
20+
strokeWidth={2}
21+
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
22+
/>
23+
</svg>
24+
),
25+
Moon: () => (
26+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
27+
<path
28+
strokeLinecap="round"
29+
strokeLinejoin="round"
30+
strokeWidth={2}
31+
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
32+
/>
33+
</svg>
34+
),
35+
Pill: () => (
36+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
37+
<path
38+
strokeLinecap="round"
39+
strokeLinejoin="round"
40+
strokeWidth={2}
41+
d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
42+
/>
43+
</svg>
44+
),
45+
Shield: () => (
46+
<svg className="w-12 h-12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
47+
<path
48+
strokeLinecap="round"
49+
strokeLinejoin="round"
50+
strokeWidth={1.5}
51+
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
52+
/>
53+
</svg>
54+
),
55+
Lightning: () => (
56+
<svg className="w-12 h-12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
57+
<path
58+
strokeLinecap="round"
59+
strokeLinejoin="round"
60+
strokeWidth={1.5}
61+
d="M13 10V3L4 14h7v7l9-11h-7z"
62+
/>
63+
</svg>
64+
),
65+
Database: () => (
66+
<svg className="w-12 h-12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
67+
<path
68+
strokeLinecap="round"
69+
strokeLinejoin="round"
70+
strokeWidth={1.5}
71+
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"
72+
/>
73+
</svg>
74+
),
75+
Close: () => (
76+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
77+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
78+
</svg>
79+
),
80+
Spinner: () => (
81+
<svg className="animate-spin w-5 h-5" fill="none" viewBox="0 0 24 24">
82+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
83+
<path
84+
className="opacity-75"
85+
fill="currentColor"
86+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
87+
/>
88+
</svg>
89+
),
90+
};
91+
92+
export default Icons;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// client/src/components/quickmed/HomePage/InfoBanner.jsx
2+
import React from 'react';
3+
4+
const InfoBanner = ({ navigateToQuickclinic }) => {
5+
return (
6+
<section className="py-12 sm:py-16">
7+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
8+
<div className="relative overflow-hidden rounded-2xl bg-gradient-to-r from-med-green-600 to-med-blue-600 p-8 sm:p-12">
9+
<div className="absolute top-0 right-0 -mt-16 -mr-16 w-64 h-64 bg-white/10 rounded-full blur-3xl" />
10+
<div className="relative flex flex-col sm:flex-row items-center justify-between gap-6">
11+
<div className="text-center sm:text-left">
12+
<h4 className="text-2xl sm:text-3xl font-bold text-white mb-2">
13+
Part of QuickClinic Ecosystem
14+
</h4>
15+
<p className="text-white/80 max-w-lg">
16+
QuickMed integrates seamlessly with QuickClinic's suite of healthcare tools for a
17+
complete medical information experience.
18+
</p>
19+
</div>
20+
<button
21+
className="flex-shrink-0 px-6 py-3 bg-white text-med-green-600 font-semibold rounded-xl hover:bg-med-green-50 transition-colors duration-200 shadow-lg"
22+
onClick={navigateToQuickclinic}
23+
>
24+
Explore QuickClinic
25+
</button>
26+
</div>
27+
</div>
28+
</div>
29+
</section>
30+
);
31+
};
32+
33+
export default InfoBanner;
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// client/src/components/quickmed/HomePage/SearchBar.jsx
2+
import React from 'react';
3+
import Icons from './Icons';
4+
5+
const SearchBar = ({
6+
searchRef,
7+
searchQuery,
8+
setSearchQuery,
9+
isLoading,
10+
showSuggestions,
11+
setShowSuggestions,
12+
suggestions,
13+
selectedIndex,
14+
setSelectedIndex,
15+
handleSelectMedicine,
16+
handleKeyDown,
17+
}) => {
18+
const popularTags = ['Aspirin', 'Ibuprofen', 'Acetaminophen', 'Amoxicillin'];
19+
20+
const handleTagClick = (tag) => {
21+
setSearchQuery(tag);
22+
setShowSuggestions(true);
23+
};
24+
25+
return (
26+
<div ref={searchRef} className="relative max-w-2xl mx-auto">
27+
{/* Search Box */}
28+
<div className="relative group">
29+
<div className="absolute -inset-1 bg-gradient-to-r from-med-green-500 to-med-blue-500 rounded-2xl blur opacity-25 group-hover:opacity-40 transition duration-300" />
30+
<div className="relative flex items-center bg-med-surface border-2 border-med-border rounded-xl overflow-hidden focus-within:border-med-green-500 transition-all duration-200">
31+
<div className="pl-5 text-med-text-muted">
32+
{isLoading ? <Icons.Spinner /> : <Icons.Search />}
33+
</div>
34+
<input
35+
type="text"
36+
value={searchQuery}
37+
onChange={(e) => {
38+
setSearchQuery(e.target.value);
39+
setShowSuggestions(true);
40+
setSelectedIndex(-1);
41+
}}
42+
onFocus={() => setShowSuggestions(true)}
43+
onKeyDown={handleKeyDown}
44+
placeholder="Search for medicines (e.g., Aspirin, Ibuprofen...)"
45+
className="w-full px-4 py-4 sm:py-5 bg-transparent text-med-text placeholder-med-text-muted focus:outline-none text-base sm:text-lg"
46+
/>
47+
{searchQuery && (
48+
<button
49+
onClick={() => {
50+
setSearchQuery('');
51+
}}
52+
className="pr-5 text-med-text-muted hover:text-med-text transition-colors"
53+
>
54+
<Icons.Close />
55+
</button>
56+
)}
57+
</div>
58+
</div>
59+
60+
{/* Suggestions Dropdown */}
61+
{showSuggestions && suggestions.length > 0 && (
62+
<div className="absolute w-full mt-2 bg-med-surface border border-med-border rounded-xl shadow-xl overflow-hidden z-50">
63+
<ul className="max-h-80 overflow-y-auto">
64+
{suggestions.map((suggestion, index) => {
65+
const name = suggestion.name || suggestion;
66+
return (
67+
<li key={index}>
68+
<button
69+
onClick={() => handleSelectMedicine(name)}
70+
className={`w-full px-5 py-3 text-left flex items-center gap-3 transition-colors duration-150 ${
71+
index === selectedIndex
72+
? 'bg-med-green-100 dark:bg-med-green-900/30 text-med-green-700 dark:text-med-green-400'
73+
: 'hover:bg-med-green-50 dark:hover:bg-med-green-900/20'
74+
}`}
75+
>
76+
<Icons.Pill />
77+
<span className="font-medium">{name}</span>
78+
</button>
79+
</li>
80+
);
81+
})}
82+
</ul>
83+
</div>
84+
)}
85+
86+
{/* No Results */}
87+
{showSuggestions && searchQuery.length >= 2 && !isLoading && suggestions.length === 0 && (
88+
<div className="absolute w-full mt-2 bg-med-surface border border-med-border rounded-xl shadow-xl p-6 text-center text-med-text-muted">
89+
No medicines found for "{searchQuery}"
90+
</div>
91+
)}
92+
93+
{/* Quick Tags */}
94+
<div className="mt-8 flex flex-wrap justify-center gap-2">
95+
<span className="text-sm text-med-text-muted">Popular searches:</span>
96+
{popularTags.map((tag) => (
97+
<button
98+
key={tag}
99+
onClick={() => handleTagClick(tag)}
100+
className="px-3 py-1 text-sm rounded-full bg-med-surface border border-med-border hover:border-med-green-500 hover:text-med-green-600 transition-all duration-200"
101+
>
102+
{tag}
103+
</button>
104+
))}
105+
</div>
106+
</div>
107+
);
108+
};
109+
110+
export default SearchBar;

0 commit comments

Comments
 (0)