Show Instagram feed and present website's privacy policies to all users

Implements Instagram feed integration with React Query in `photo-gallery.tsx`, adds `/api/instagram-feed` route in `server/routes.ts`, and creates a new `PrivacyPolicyPage` component.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: e4277a6e-587a-496e-8663-733446748f62
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/af8dabca-e746-4e53-9c29-d8d4d9cf30f5/6067ab3b-1969-480a-a76e-9230d9efb53f.jpg
This commit is contained in:
JeffEmmett 2025-06-15 13:26:09 +00:00
parent ea2268f988
commit 0df2e6b9ff
4 changed files with 415 additions and 18 deletions

View File

@ -0,0 +1,152 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Privacy Policy - Pilates with Fadia</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f9f9f9;
}
.container {
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #2c5282;
border-bottom: 3px solid #4299e1;
padding-bottom: 10px;
margin-bottom: 30px;
}
h2 {
color: #2d3748;
margin-top: 30px;
margin-bottom: 15px;
}
.last-updated {
background: #e2e8f0;
padding: 10px;
border-radius: 5px;
margin-bottom: 30px;
font-style: italic;
}
.contact-info {
background: #f7fafc;
padding: 15px;
border-left: 4px solid #4299e1;
margin: 20px 0;
}
ul {
padding-left: 20px;
}
li {
margin-bottom: 8px;
}
</style>
</head>
<body>
<div class="container">
<h1>Privacy Policy</h1>
<div class="last-updated">
<strong>Last Updated:</strong> [INSERT DATE]
</div>
<p>At Pilates with Fadia ("we," "our," or "us"), we are committed to protecting your privacy and personal information. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website pilateswithfadia.com and use our services.</p>
<h2>1. Information We Collect</h2>
<h3>Personal Information</h3>
<p>We may collect personal information that you voluntarily provide to us, including:</p>
<ul>
<li>Name and contact information (email address, phone number, mailing address)</li>
<li>Account credentials (username and password)</li>
<li>Payment information (processed securely through third-party payment processors)</li>
<li>Health and fitness information relevant to your Pilates practice</li>
<li>Class preferences and booking history</li>
<li>Communication preferences</li>
</ul>
<h3>Automatically Collected Information</h3>
<p>When you visit our website, we may automatically collect:</p>
<ul>
<li>IP address and location data</li>
<li>Browser type and version</li>
<li>Device information</li>
<li>Pages visited and time spent on our site</li>
<li>Referring website information</li>
</ul>
<h2>2. How We Use Your Information</h2>
<p>We use your information to:</p>
<ul>
<li>Provide and improve our Pilates services and classes</li>
<li>Process bookings and payments</li>
<li>Send class schedules, updates, and important notifications</li>
<li>Respond to your inquiries and provide customer support</li>
<li>Personalize your experience and class recommendations</li>
<li>Send marketing communications (with your consent)</li>
<li>Ensure the safety and security of our services</li>
<li>Comply with legal obligations</li>
</ul>
<h2>3. Information Sharing and Disclosure</h2>
<p>We do not sell, trade, or rent your personal information to third parties. We may share your information in the following circumstances:</p>
<ul>
<li><strong>Service Providers:</strong> With trusted third-party service providers who assist us in operating our website and providing services</li>
<li><strong>Legal Requirements:</strong> When required by law or to protect our rights and safety</li>
<li><strong>Business Transfers:</strong> In connection with a merger, acquisition, or sale of assets</li>
<li><strong>Consent:</strong> With your explicit consent for specific purposes</li>
</ul>
<h2>4. Data Security</h2>
<p>We implement appropriate technical and organizational security measures to protect your personal information against unauthorized access, alteration, disclosure, or destruction. However, no method of transmission over the internet is 100% secure.</p>
<h2>5. Your Rights and Choices</h2>
<p>You have the right to:</p>
<ul>
<li>Access, update, or delete your personal information</li>
<li>Opt-out of marketing communications</li>
<li>Request data portability</li>
<li>Object to certain data processing activities</li>
<li>Withdraw consent at any time</li>
</ul>
<h2>6. Cookies and Tracking Technologies</h2>
<p>We use cookies and similar tracking technologies to enhance your browsing experience. Please refer to our Cookie Policy for detailed information about our use of cookies.</p>
<h2>7. Third-Party Links</h2>
<p>Our website may contain links to third-party websites. We are not responsible for the privacy practices of these external sites. We encourage you to review their privacy policies.</p>
<h2>8. Children's Privacy</h2>
<p>Our services are not intended for children under 13. We do not knowingly collect personal information from children under 13. If you are a parent or guardian and believe your child has provided us with personal information, please contact us.</p>
<h2>9. International Data Transfers</h2>
<p>Your information may be transferred to and processed in countries other than your own. We ensure appropriate safeguards are in place for such transfers.</p>
<h2>10. Data Retention</h2>
<p>We retain your personal information only as long as necessary to fulfill the purposes outlined in this Privacy Policy or as required by law.</p>
<h2>11. Changes to This Privacy Policy</h2>
<p>We may update this Privacy Policy from time to time. We will notify you of any material changes by posting the new policy on our website and updating the "Last Updated" date.</p>
<h2>12. Contact Us</h2>
<div class="contact-info">
<p>If you have any questions about this Privacy Policy or our privacy practices, please contact us:</p>
<p><strong>Pilates with Fadia</strong><br>
Email: [INSERT EMAIL]<br>
Phone: [INSERT PHONE]<br>
Address: [INSERT ADDRESS]<br>
Website: pilateswithfadia.com</p>
</div>
</div>
</body>
</html>

View File

@ -1,4 +1,25 @@
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
interface InstagramPost {
id: string;
media_type: 'IMAGE' | 'VIDEO' | 'CAROUSEL_ALBUM';
media_url: string;
permalink: string;
caption?: string;
timestamp: string;
}
export function PhotoGallery() {
const [showAllPosts, setShowAllPosts] = useState(false);
const { data: posts, isLoading, error } = useQuery<InstagramPost[]>({
queryKey: ['/api/instagram-feed'],
staleTime: 1000 * 60 * 30, // Cache for 30 minutes
});
const displayPosts = showAllPosts ? posts : posts?.slice(0, 6);
return (
<section className="py-12 bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@ -10,29 +31,94 @@ export function PhotoGallery() {
<div className="flex justify-center">
<div className="w-full max-w-4xl">
{/* Instagram Feed Embed */}
<div className="rounded-lg p-8 text-center bg-[#a3587554]">
<div className="mb-6">
<i className="fab fa-instagram text-6xl text-pink-500 mb-4"></i>
<p className="text-gray-600 mb-6">Follow me on Instagram for daily inspiration, movement tips, and behind-the-scenes content from my classes.</p>
{isLoading && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{[1, 2, 3, 4, 5, 6].map((index) => (
<div key={index} className="aspect-square bg-gray-200 rounded-lg animate-pulse"></div>
))}
</div>
{/* Instagram embed placeholder - this would typically be replaced with actual Instagram embed code */}
<div className="mt-8 bg-white rounded-lg border border-gray-200 p-6">
)}
{error && (
<div className="bg-gray-50 rounded-lg p-8 text-center">
<div className="mb-6">
<i className="fab fa-instagram text-6xl text-pink-500 mb-4"></i>
<p className="text-gray-600 mb-6">Follow me on Instagram for daily inspiration, movement tips, and behind-the-scenes content from my classes.</p>
<a
href="https://www.instagram.com/pilateswithfadia/"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-purple-500 to-pink-500 text-white font-semibold rounded-full hover:from-purple-600 hover:to-pink-600 transition duration-300"
>
<i className="fab fa-instagram mr-2"></i>
Visit Instagram
</a>
</div>
</div>
)}
{posts && posts.length > 0 && (
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{[1, 2, 3, 4, 5, 6].map((index) => (
<div key={index} className="aspect-square bg-gray-100 rounded-lg flex items-center justify-center">
<i className="fab fa-instagram text-3xl text-gray-400"></i>
</div>
{displayPosts?.map((post: InstagramPost) => (
<a
key={post.id}
href={post.permalink}
target="_blank"
rel="noopener noreferrer"
className="group block aspect-square bg-gray-100 rounded-lg overflow-hidden hover:shadow-lg transition-shadow duration-300"
>
{post.media_type === 'VIDEO' ? (
<div className="relative w-full h-full">
<video
src={post.media_url}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
muted
loop
playsInline
onMouseEnter={(e) => e.currentTarget.play()}
onMouseLeave={(e) => e.currentTarget.pause()}
/>
<div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-10 transition-all duration-300 flex items-center justify-center">
<i className="fas fa-play text-white text-2xl opacity-0 group-hover:opacity-80 transition-opacity duration-300"></i>
</div>
</div>
) : (
<img
src={post.media_url}
alt={post.caption ? post.caption.substring(0, 100) + '...' : 'Instagram post'}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
loading="lazy"
/>
)}
</a>
))}
</div>
<p className="text-sm text-gray-500 mt-4">
Instagram feed will display here. For the best experience, visit my Instagram profile directly.
</p>
{posts.length > 6 && (
<div className="text-center">
<button
onClick={() => setShowAllPosts(!showAllPosts)}
className="px-6 py-3 bg-rose text-white font-semibold rounded-full hover:bg-opacity-90 transition duration-300"
>
{showAllPosts ? 'Show Less' : `View All ${posts.length} Posts`}
</button>
</div>
)}
<div className="text-center mt-8">
<a
href="https://www.instagram.com/pilateswithfadia/"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-purple-500 to-pink-500 text-white font-semibold rounded-full hover:from-purple-600 hover:to-pink-600 transition duration-300"
>
<i className="fab fa-instagram mr-2"></i>
Follow on Instagram
</a>
</div>
</div>
</div>
)}
</div>
</div>
</div>

View File

@ -0,0 +1,121 @@
export default function PrivacyPolicyPage() {
return (
<div className="min-h-screen bg-gray-50 py-12">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="bg-white rounded-lg shadow-lg p-8 md:p-12">
<h1 className="text-3xl md:text-4xl font-playfair font-bold text-teal mb-6 pb-4 border-b-4 border-teal">
Privacy Policy
</h1>
<div className="bg-gray-100 p-4 rounded-lg mb-8 italic">
<strong>Last Updated:</strong> December 2024
</div>
<p className="text-gray-700 mb-6 leading-relaxed">
At Pilates with Fadia ("we," "our," or "us"), we are committed to protecting your privacy and personal information. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website pilateswithfadia.com and use our services.
</p>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">1. Information We Collect</h2>
<h3 className="text-xl font-semibold text-gray-700 mb-3">Personal Information</h3>
<p className="text-gray-700 mb-3">We may collect personal information that you voluntarily provide to us, including:</p>
<ul className="list-disc list-inside text-gray-700 mb-6 space-y-2 ml-4">
<li>Name and contact information (email address, phone number, mailing address)</li>
<li>Account credentials (username and password)</li>
<li>Payment information (processed securely through third-party payment processors)</li>
<li>Health and fitness information relevant to your Pilates practice</li>
<li>Class preferences and booking history</li>
<li>Communication preferences</li>
</ul>
<h3 className="text-xl font-semibold text-gray-700 mb-3">Automatically Collected Information</h3>
<p className="text-gray-700 mb-3">When you visit our website, we may automatically collect:</p>
<ul className="list-disc list-inside text-gray-700 mb-6 space-y-2 ml-4">
<li>IP address and location data</li>
<li>Browser type and version</li>
<li>Device information</li>
<li>Pages visited and time spent on our site</li>
<li>Referring website information</li>
</ul>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">2. How We Use Your Information</h2>
<p className="text-gray-700 mb-3">We use your information to:</p>
<ul className="list-disc list-inside text-gray-700 mb-6 space-y-2 ml-4">
<li>Provide and improve our Pilates services and classes</li>
<li>Process bookings and payments</li>
<li>Send class schedules, updates, and important notifications</li>
<li>Respond to your inquiries and provide customer support</li>
<li>Personalize your experience and class recommendations</li>
<li>Send marketing communications (with your consent)</li>
<li>Ensure the safety and security of our services</li>
<li>Comply with legal obligations</li>
</ul>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">3. Information Sharing and Disclosure</h2>
<p className="text-gray-700 mb-3">We do not sell, trade, or rent your personal information to third parties. We may share your information in the following circumstances:</p>
<ul className="list-disc list-inside text-gray-700 mb-6 space-y-2 ml-4">
<li><strong>Service Providers:</strong> With trusted third-party service providers who assist us in operating our website and providing services</li>
<li><strong>Legal Requirements:</strong> When required by law or to protect our rights and safety</li>
<li><strong>Business Transfers:</strong> In connection with a merger, acquisition, or sale of assets</li>
<li><strong>Consent:</strong> With your explicit consent for specific purposes</li>
</ul>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">4. Data Security</h2>
<p className="text-gray-700 mb-6">
We implement appropriate technical and organizational security measures to protect your personal information against unauthorized access, alteration, disclosure, or destruction. However, no method of transmission over the internet is 100% secure.
</p>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">5. Your Rights and Choices</h2>
<p className="text-gray-700 mb-3">You have the right to:</p>
<ul className="list-disc list-inside text-gray-700 mb-6 space-y-2 ml-4">
<li>Access, update, or delete your personal information</li>
<li>Opt-out of marketing communications</li>
<li>Request data portability</li>
<li>Object to certain data processing activities</li>
<li>Withdraw consent at any time</li>
</ul>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">6. Cookies and Tracking Technologies</h2>
<p className="text-gray-700 mb-6">
We use cookies and similar tracking technologies to enhance your browsing experience. Please refer to our Cookie Policy for detailed information about our use of cookies.
</p>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">7. Third-Party Links</h2>
<p className="text-gray-700 mb-6">
Our website may contain links to third-party websites. We are not responsible for the privacy practices of these external sites. We encourage you to review their privacy policies.
</p>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">8. Children's Privacy</h2>
<p className="text-gray-700 mb-6">
Our services are not intended for children under 13. We do not knowingly collect personal information from children under 13. If you are a parent or guardian and believe your child has provided us with personal information, please contact us.
</p>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">9. International Data Transfers</h2>
<p className="text-gray-700 mb-6">
Your information may be transferred to and processed in countries other than your own. We ensure appropriate safeguards are in place for such transfers.
</p>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">10. Data Retention</h2>
<p className="text-gray-700 mb-6">
We retain your personal information only as long as necessary to fulfill the purposes outlined in this Privacy Policy or as required by law.
</p>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">11. Changes to This Privacy Policy</h2>
<p className="text-gray-700 mb-6">
We may update this Privacy Policy from time to time. We will notify you of any material changes by posting the new policy on our website and updating the "Last Updated" date.
</p>
<h2 className="text-2xl font-semibold text-gray-800 mt-8 mb-4">12. Contact Us</h2>
<div className="bg-blue-50 p-6 rounded-lg border-l-4 border-teal">
<p className="text-gray-700 mb-4">If you have any questions about this Privacy Policy or our privacy practices, please contact us:</p>
<div className="text-gray-700">
<p><strong>Pilates with Fadia</strong></p>
<p>Email: hello@pilateswithfadia.com</p>
<p>Website: pilateswithfadia.com</p>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -143,6 +143,44 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
// Instagram Feed
app.get("/api/instagram-feed", async (_req, res) => {
try {
const instagramAccessToken = process.env.INSTAGRAM_ACCESS_TOKEN;
if (!instagramAccessToken) {
return res.status(500).json({
message: "Instagram access token not configured",
error: "INSTAGRAM_ACCESS_TOKEN environment variable is required"
});
}
// Fetch recent posts from Instagram Basic Display API
const response = await fetch(
`https://graph.instagram.com/me/media?fields=id,media_type,media_url,permalink,caption,timestamp&access_token=${instagramAccessToken}`
);
if (!response.ok) {
throw new Error(`Instagram API error: ${response.status} ${response.statusText}`);
}
const data = await response.json();
// Filter out only images and videos, exclude carousels for simplicity
const posts = data.data?.filter((post: any) =>
post.media_type === 'IMAGE' || post.media_type === 'VIDEO'
).slice(0, 12) || []; // Limit to 12 most recent posts
res.json(posts);
} catch (error) {
console.error('Instagram API error:', error);
res.status(500).json({
message: "Failed to fetch Instagram posts",
error: error instanceof Error ? error.message : "Unknown error"
});
}
});
const httpServer = createServer(app);
return httpServer;
}