feat(beta): add beta testing features and privacy policy page

Implemented comprehensive beta testing system with tier badges and
reorganized environment configuration for better maintainability.

Beta Testing Features:
- Beta banner component with dismissible state (localStorage)
- Auto-assign SUPPORTER tier to new registrations (env controlled)
- TierBadge component with SUPPORTER/COMFORT tier display
- Badge shown in Navbar, ProfilePage, and PublicProfilePage
- Environment variables: VITE_BETA_MODE, BETA_AUTO_SUPPORTER

Environment Configuration Reorganization:
- Moved .env files from root to frontend/ and backend/ directories
- Created .env.{development,production}{,.example} structure
- Updated docker-compose.yml to use env_file for frontend
- All env vars properly namespaced and documented

Privacy Policy Implementation:
- New /privacy route with dedicated PrivacyPage component
- Comprehensive GDPR/RODO compliant privacy policy (privacy.html)
- Updated CookieConsent banner to link to /privacy
- Added Privacy Policy links to all footers (HomePage, PublicFooter)
- Removed privacy section from About Us page

HTML Content System:
- Replaced react-markdown dependency with simple HTML loader
- New HtmlContentPage component for rendering .html files
- Converted about-us.md and how-it-works.md to .html format
- Inline CSS support for full styling control
- Easier content editing without React knowledge

Backend Changes:
- Registration auto-assigns SUPPORTER tier when BETA_AUTO_SUPPORTER=true
- Added accountTier to auth middleware and user routes
- Updated public profile endpoint to include accountTier

Files:
- Added: frontend/.env.{development,production}{,.example}
- Added: backend/.env variables for BETA_AUTO_SUPPORTER
- Added: components/BetaBanner.jsx, TierBadge.jsx, HtmlContentPage.jsx
- Added: pages/PrivacyPage.jsx
- Added: public/content/{about-us,how-it-works,privacy}.html
- Modified: docker-compose.yml (env_file configuration)
- Modified: App.jsx (privacy route, beta banner)
- Modified: auth.js (auto SUPPORTER tier logic)
This commit is contained in:
Radosław Gierwiało
2025-12-06 11:50:28 +01:00
parent a786b1d92d
commit e2b10387c2
28 changed files with 841 additions and 251 deletions

View File

@@ -1,91 +1,5 @@
import { useState, useEffect } from 'react';
import ReactMarkdown from 'react-markdown';
import PublicLayout from '../components/layout/PublicLayout';
import HtmlContentPage from '../components/HtmlContentPage';
export default function HowItWorksPage() {
const [content, setContent] = useState('');
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => {
// Fetch markdown content
fetch('/content/how-it-works.md')
.then(response => {
if (!response.ok) {
throw new Error('Failed to load content');
}
return response.text();
})
.then(text => {
setContent(text);
setLoading(false);
})
.catch(err => {
console.error('Error loading how-it-works content:', err);
setError('Failed to load page content');
setLoading(false);
});
}, []);
if (loading) {
return (
<PublicLayout>
<div className="bg-gray-50 flex items-center justify-center py-12">
<div className="text-center">
<div className="w-12 h-12 border-4 border-primary-600 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
<p className="text-gray-600">Loading...</p>
</div>
</div>
</PublicLayout>
);
}
if (error) {
return (
<PublicLayout>
<div className="bg-gray-50 flex items-center justify-center py-12">
<div className="text-center">
<p className="text-red-600">{error}</p>
</div>
</div>
</PublicLayout>
);
}
return (
<PublicLayout>
<div className="bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-4xl mx-auto">
<article className="bg-white rounded-lg shadow-sm p-8 md:p-12 prose prose-lg max-w-none">
<ReactMarkdown
components={{
// Customize link rendering to use React Router for internal links
a: ({ node, href, children, ...props }) => {
const isInternal = href && href.startsWith('/');
if (isInternal) {
return (
<a href={href} {...props}>
{children}
</a>
);
}
return (
<a href={href} target="_blank" rel="noopener noreferrer" {...props}>
{children}
</a>
);
},
// Ensure images are responsive
img: ({ node, ...props }) => (
<img className="rounded-lg shadow-md" {...props} alt={props.alt || ''} />
),
}}
>
{content}
</ReactMarkdown>
</article>
</div>
</div>
</PublicLayout>
);
return <HtmlContentPage contentFile="how-it-works.html" pageTitle="How It Works" />;
}