Initial commit
This commit is contained in:
227
src/pages/HomePage.tsx
Normal file
227
src/pages/HomePage.tsx
Normal file
@@ -0,0 +1,227 @@
|
||||
import AboutText from '@/components/sections/about/AboutText';
|
||||
import ContactCta from '@/components/sections/contact/ContactCta';
|
||||
import FaqTwoColumn from '@/components/sections/faq/FaqTwoColumn';
|
||||
import FeaturesTaggedCards from '@/components/sections/features/FeaturesTaggedCards';
|
||||
import HeroCenteredLogos from '@/components/sections/hero/HeroCenteredLogos';
|
||||
import MetricsFeatureCards from '@/components/sections/metrics/MetricsFeatureCards';
|
||||
import TestimonialMarqueeCards from '@/components/sections/testimonial/TestimonialMarqueeCards';
|
||||
import SectionErrorBoundary from "@/components/ui/SectionErrorBoundary";
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<>
|
||||
<div id="hero" data-section="hero">
|
||||
<SectionErrorBoundary name="hero">
|
||||
<HeroCenteredLogos
|
||||
avatarsSrc={[
|
||||
"http://img.b2bpic.net/free-photo/elderly-couple-talking_23-2148116315.jpg",
|
||||
"http://img.b2bpic.net/free-photo/woman-doing-gymnastics-with-help-his-young-physical-therapist_169016-43495.jpg",
|
||||
"http://img.b2bpic.net/free-photo/retired-woman-browsing-internet-digital-tablet-home-elder-person-with-crutches-sofa-using-modern-gadget-with-touch-screen-learn-technology-living-room-pensioner-with-device_482257-40616.jpg",
|
||||
"http://img.b2bpic.net/free-photo/medical-assistant-helping-patient-with-physiotherapy-exercises_23-2149071465.jpg",
|
||||
"http://img.b2bpic.net/free-photo/people-watching-news-tv_23-2149718956.jpg",
|
||||
]}
|
||||
avatarText="Trusted by 60+ patients"
|
||||
title="Professional Home Physiotherapy & Cupping"
|
||||
description="Expert physiotherapy, hijama, and therapeutic massage delivered in the comfort of your home. Professional care with a personal touch."
|
||||
primaryButton={{
|
||||
text: "Book Now",
|
||||
href: "#contact",
|
||||
}}
|
||||
secondaryButton={{
|
||||
text: "View Reviews",
|
||||
href: "#reviews",
|
||||
}}
|
||||
names={[
|
||||
"Ahmed",
|
||||
"Sara",
|
||||
"Khalid",
|
||||
"Mona",
|
||||
"Omar",
|
||||
]}
|
||||
imageSrc="http://img.b2bpic.net/free-photo/female-therapist-rehabilitation-center-giving-back-massage_23-2150356750.jpg"
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div id="about" data-section="about">
|
||||
<SectionErrorBoundary name="about">
|
||||
<AboutText
|
||||
title="Providing Golden Hand Healing at Your Home"
|
||||
primaryButton={{
|
||||
text: "Learn More",
|
||||
href: "#services",
|
||||
}}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div id="services" data-section="services">
|
||||
<SectionErrorBoundary name="services">
|
||||
<FeaturesTaggedCards
|
||||
tag="Our Services"
|
||||
title="Comprehensive Recovery Care"
|
||||
description="We specialize in bringing clinical quality treatments to your door."
|
||||
items={[
|
||||
{
|
||||
tag: "Physical Therapy",
|
||||
title: "Physiotherapy",
|
||||
description: "Tailored exercises and rehabilitation plans for your specific recovery journey.",
|
||||
primaryButton: {
|
||||
text: "Learn more",
|
||||
href: "#",
|
||||
},
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/close-up-rehab-doctor-hands-massaging-patient-shoulder-shoulder-therapy_169016-71210.jpg",
|
||||
},
|
||||
{
|
||||
tag: "Cupping",
|
||||
title: "Hijama & Cupping",
|
||||
description: "Professional hijama and dry cupping services for pain relief and improved circulation.",
|
||||
primaryButton: {
|
||||
text: "Learn more",
|
||||
href: "#",
|
||||
},
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/hand-putting-suction-cups-woman-s-back_23-2149406810.jpg",
|
||||
},
|
||||
{
|
||||
tag: "Massage",
|
||||
title: "Therapeutic Massage",
|
||||
description: "Professional deep tissue and recovery massage to soothe muscles and reduce stress.",
|
||||
primaryButton: {
|
||||
text: "Learn more",
|
||||
href: "#",
|
||||
},
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/leg-massage-with-oil-long-gliding-strokes-daylight-spa-ease_169016-69622.jpg",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div id="stats" data-section="stats">
|
||||
<SectionErrorBoundary name="stats">
|
||||
<MetricsFeatureCards
|
||||
tag="Our Experience"
|
||||
title="Proven Results"
|
||||
description="Consistently providing 5-star rated care for every patient."
|
||||
metrics={[
|
||||
{
|
||||
value: "4.9/5",
|
||||
title: "Average Rating",
|
||||
features: [
|
||||
"63+ Reviews",
|
||||
"Top-rated service",
|
||||
"Reliable",
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "100+",
|
||||
title: "Cases Handled",
|
||||
features: [
|
||||
"Rehabilitation",
|
||||
"Pain Relief",
|
||||
"Post-op",
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "24/7",
|
||||
title: "Support",
|
||||
features: [
|
||||
"Flexible Timing",
|
||||
"Home Visit",
|
||||
"Available",
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div id="reviews" data-section="reviews">
|
||||
<SectionErrorBoundary name="reviews">
|
||||
<TestimonialMarqueeCards
|
||||
tag="Testimonials"
|
||||
title="What Our Patients Say"
|
||||
description="Trusted by hundreds across the country."
|
||||
testimonials={[
|
||||
{
|
||||
name: "Abdulrahman",
|
||||
role: "Patient",
|
||||
quote: "Dr. Gamal is excellent. His hands are magic in massage and cupping.",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/patient-consulting-doctor_1170-2096.jpg",
|
||||
},
|
||||
{
|
||||
name: "Safa",
|
||||
role: "Patient",
|
||||
quote: "Professional in every way. Dr. Gamal has a golden hand.",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/unrecognizable-male-doctor-writing-medical-report-while-visiting-senior-couple-their-home_637285-1354.jpg",
|
||||
},
|
||||
{
|
||||
name: "Ahmed Ali",
|
||||
role: "Hotel Guest",
|
||||
quote: "Very professional, came to my hotel and then my apartment.",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/vertical-shot-female-doctor-listening-elderly-heartbeat_181624-46401.jpg",
|
||||
},
|
||||
{
|
||||
name: "Khalid",
|
||||
role: "Patient",
|
||||
quote: "Best recovery massage I have ever had at home.",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/doctor-s-supportin-their-patient_23-2152020805.jpg",
|
||||
},
|
||||
{
|
||||
name: "Mona",
|
||||
role: "Patient",
|
||||
quote: "Responsive and skilled, highly recommend for physiotherapy.",
|
||||
imageSrc: "http://img.b2bpic.net/free-photo/front-view-female-doctor-medical-shirt-rejoicing-yellow-background_179666-31447.jpg",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div id="faq" data-section="faq">
|
||||
<SectionErrorBoundary name="faq">
|
||||
<FaqTwoColumn
|
||||
tag="Support"
|
||||
title="Frequently Asked Questions"
|
||||
description="Everything you need to know about our home visit services."
|
||||
items={[
|
||||
{
|
||||
question: "Do you offer services on weekends?",
|
||||
answer: "Yes, we operate 24/7 and provide services on weekends and public holidays.",
|
||||
},
|
||||
{
|
||||
question: "What equipment do you bring?",
|
||||
answer: "We bring all necessary clinical equipment for physiotherapy, cupping, and massage.",
|
||||
},
|
||||
{
|
||||
question: "Where do you provide service?",
|
||||
answer: "We cover Cairo, Sheikh Zayed, 6th of October, and surrounding areas.",
|
||||
},
|
||||
{
|
||||
question: "How to book an appointment?",
|
||||
answer: "Simply contact us via phone or WhatsApp at the provided number to schedule your visit.",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
|
||||
<div id="contact" data-section="contact">
|
||||
<SectionErrorBoundary name="contact">
|
||||
<ContactCta
|
||||
tag="Book Now"
|
||||
text="Ready to start your recovery? Contact Dr. Gamal for a professional home visit today."
|
||||
primaryButton={{
|
||||
text: "Call 015 52557597",
|
||||
href: "tel:01552557597",
|
||||
}}
|
||||
secondaryButton={{
|
||||
text: "WhatsApp Us",
|
||||
href: "https://wa.me/201552557597",
|
||||
}}
|
||||
/>
|
||||
</SectionErrorBoundary>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
13
src/pages/blog/BlogPage.tsx
Normal file
13
src/pages/blog/BlogPage.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import BlogSimpleCards from "@/components/sections/blog/BlogSimpleCards";
|
||||
|
||||
const BlogPage = () => {
|
||||
return (
|
||||
<BlogSimpleCards
|
||||
tag="Blog"
|
||||
title="Latest Articles"
|
||||
description="Stay updated with our latest insights and news"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlogPage;
|
||||
50
src/pages/blog/BlogPostPage.tsx
Normal file
50
src/pages/blog/BlogPostPage.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import BlogArticle from "@/components/sections/blog/BlogArticle";
|
||||
import useBlogPost from "@/hooks/useBlogPost";
|
||||
|
||||
const BlogPostPage = () => {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { post, isLoading } = useBlogPost(slug || "");
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<section className="w-content-width mx-auto py-20">
|
||||
<div className="flex justify-center">
|
||||
<Loader2 className="size-8 animate-spin text-foreground" strokeWidth={1.5} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
if (!post || !post.content) {
|
||||
return (
|
||||
<section className="w-content-width mx-auto py-20 text-center">
|
||||
<p className="text-foreground mb-4">Post not found</p>
|
||||
<button onClick={() => navigate("/blog")} className="primary-button px-6 py-2 rounded-theme text-primary-cta-text">
|
||||
Back to Blog
|
||||
</button>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const wordCount = post.content.replace(/<[^>]*>/g, " ").trim().split(/\s+/).length;
|
||||
const readingTime = `${Math.max(1, Math.round(wordCount / 200))} min read`;
|
||||
|
||||
return (
|
||||
<BlogArticle
|
||||
category={post.category}
|
||||
title={post.title}
|
||||
excerpt={post.excerpt}
|
||||
content={post.content}
|
||||
imageSrc={post.imageSrc}
|
||||
authorName={post.authorName}
|
||||
authorImageSrc={post.authorAvatar}
|
||||
date={post.date}
|
||||
readingTime={readingTime}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlogPostPage;
|
||||
79
src/pages/shop/ProductPage.tsx
Normal file
79
src/pages/shop/ProductPage.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { ReactLenis } from "lenis/react";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import ProductDetailCard from "@/components/ecommerce/ProductDetailCard";
|
||||
import ProductCart from "@/components/ecommerce/ProductCart";
|
||||
import useProductDetail from "@/hooks/useProductDetail";
|
||||
import useCart from "@/hooks/useCart";
|
||||
import useCheckout from "@/hooks/useCheckout";
|
||||
|
||||
const ProductPage = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { product, isLoading, images, createCartItem, selectedQuantity } = useProductDetail(id || "");
|
||||
const { items: cartItems, isOpen: cartOpen, setIsOpen: setCartOpen, addItem, updateQuantity, removeItem, total: cartTotal, getCheckoutItems } = useCart();
|
||||
const { buyNow, checkout } = useCheckout();
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<section className="w-content-width mx-auto py-20">
|
||||
<div className="flex justify-center">
|
||||
<Loader2 className="size-8 animate-spin text-foreground" strokeWidth={1.5} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
if (!product) {
|
||||
return (
|
||||
<section className="w-content-width mx-auto py-20 text-center">
|
||||
<p className="text-foreground mb-4">Product not found</p>
|
||||
<button onClick={() => navigate("/shop")} className="primary-button px-6 py-2 rounded-theme text-primary-cta-text">
|
||||
Back to Shop
|
||||
</button>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const handleAddToCart = () => {
|
||||
const item = createCartItem();
|
||||
if (item) addItem(item);
|
||||
};
|
||||
|
||||
const handleBuyNow = () => {
|
||||
buyNow(product, selectedQuantity);
|
||||
};
|
||||
|
||||
const handleCheckout = async () => {
|
||||
if (cartItems.length === 0) return;
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set("success", "true");
|
||||
await checkout(getCheckoutItems(), { successUrl: url.toString() });
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactLenis root>
|
||||
<ProductDetailCard
|
||||
name={product.name}
|
||||
price={product.price}
|
||||
description={product.description}
|
||||
images={images.map((img) => img.src)}
|
||||
rating={product.rating}
|
||||
onAddToCart={handleAddToCart}
|
||||
onBuyNow={handleBuyNow}
|
||||
/>
|
||||
<ProductCart
|
||||
isOpen={cartOpen}
|
||||
onClose={() => setCartOpen(false)}
|
||||
items={cartItems}
|
||||
total={`$${cartTotal}`}
|
||||
onQuantityChange={updateQuantity}
|
||||
onRemove={removeItem}
|
||||
onCheckout={handleCheckout}
|
||||
/>
|
||||
</ReactLenis>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductPage;
|
||||
31
src/pages/shop/ShopPage.tsx
Normal file
31
src/pages/shop/ShopPage.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import ProductCatalog from "@/components/ecommerce/ProductCatalog";
|
||||
import useProductCatalog from "@/hooks/useProductCatalog";
|
||||
|
||||
const ShopPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const { products, isLoading, search, setSearch } = useProductCatalog({
|
||||
onProductClick: (productId) => navigate(`/shop/${productId}`),
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<section className="w-content-width mx-auto py-20">
|
||||
<div className="flex justify-center">
|
||||
<Loader2 className="size-8 animate-spin text-foreground" strokeWidth={1.5} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ProductCatalog
|
||||
products={products}
|
||||
searchValue={search}
|
||||
onSearchChange={setSearch}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShopPage;
|
||||
Reference in New Issue
Block a user