feat: Containerize services with Docker Compose (PostgreSQL, Strapi CMS, Express Backend, Nginx Frontend) on custom ports >9000
This commit is contained in:
+96
-14
@@ -12,6 +12,9 @@ import AboutUsPage from './components/AboutUsPage';
|
||||
import TurnstileCaptcha from './components/TurnstileCaptcha';
|
||||
import GlassmorphicSelect from './components/GlassmorphicSelect';
|
||||
import GlassmorphicDatePicker from './components/GlassmorphicDatePicker';
|
||||
import BlogDetailPage from './components/BlogDetailPage';
|
||||
import CareersPage from './components/CareersPage';
|
||||
import BlogsPage from './components/BlogsPage';
|
||||
|
||||
// Reusable Scroll Reveal Wrapper with Reduced Motion Support
|
||||
function Reveal({ children, delay = 0, style }) {
|
||||
@@ -405,24 +408,60 @@ export default function App({ previewMode = false }) {
|
||||
scrollTimeout = setTimeout(handleScrollEnd, 800);
|
||||
};
|
||||
|
||||
const [selectedBlog, setSelectedBlog] = useState(null);
|
||||
|
||||
const handleSelectBlog = (blog) => {
|
||||
setNavbarVisible(true);
|
||||
setMobileMenuOpen(false);
|
||||
setSelectedBlog(blog);
|
||||
setCurrentPage('blog');
|
||||
window.location.hash = blog?.slug ? `blog-${blog.slug}` : 'blog';
|
||||
smoothScrollTo(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const syncPageFromHash = () => {
|
||||
const hash = window.location.hash;
|
||||
if (hash === '#contact' || hash === '#contactus') {
|
||||
const syncPageFromHash = (shouldScroll = false) => {
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
if (hash === 'contact' || hash === 'contactus') {
|
||||
setSelectedBlog(null);
|
||||
setCurrentPage('contact');
|
||||
setActiveSection('contactus');
|
||||
smoothScrollTo(0);
|
||||
} else if (hash === '#about' || hash === '#aboutus') {
|
||||
if (shouldScroll) smoothScrollTo(0);
|
||||
} else if (hash === 'about' || hash === 'aboutus') {
|
||||
setSelectedBlog(null);
|
||||
setCurrentPage('about');
|
||||
setActiveSection('aboutus');
|
||||
smoothScrollTo(0);
|
||||
} else if (hash === '#home' || hash === '') {
|
||||
if (shouldScroll) smoothScrollTo(0);
|
||||
} else if (hash === 'careers') {
|
||||
setSelectedBlog(null);
|
||||
setCurrentPage('careers');
|
||||
setActiveSection('careers');
|
||||
if (shouldScroll) smoothScrollTo(0);
|
||||
} else if (hash === 'blogs') {
|
||||
setSelectedBlog(null);
|
||||
setCurrentPage('blogs');
|
||||
setActiveSection('blogs');
|
||||
if (shouldScroll) smoothScrollTo(0);
|
||||
} else if (hash.startsWith('blog-') || hash === 'blog') {
|
||||
const slug = hash.replace('blog-', '');
|
||||
const found = insights.find(b => b.slug === slug || String(b.id) === slug);
|
||||
if (found) {
|
||||
setSelectedBlog(prev => (prev && (prev.id === found.id || (prev.slug && prev.slug === found.slug)) ? prev : found));
|
||||
} else if (insights && insights.length > 0) {
|
||||
setSelectedBlog(prev => prev || insights[0]);
|
||||
}
|
||||
setCurrentPage('blog');
|
||||
if (shouldScroll) smoothScrollTo(0);
|
||||
} else if (hash === 'home' || hash === '') {
|
||||
setSelectedBlog(null);
|
||||
setCurrentPage('home');
|
||||
}
|
||||
};
|
||||
window.addEventListener('hashchange', syncPageFromHash);
|
||||
return () => window.removeEventListener('hashchange', syncPageFromHash);
|
||||
}, []);
|
||||
syncPageFromHash(false);
|
||||
const handleHashChange = () => syncPageFromHash(true);
|
||||
window.addEventListener('hashchange', handleHashChange);
|
||||
return () => window.removeEventListener('hashchange', handleHashChange);
|
||||
}, [insights]);
|
||||
const [hoveredItem, setHoveredItem] = useState(null);
|
||||
const [showCareersAcknowledgement, setShowCareersAcknowledgement] = useState(false);
|
||||
const [isStrategyOpen, setIsStrategyOpen] = useState(false);
|
||||
@@ -539,6 +578,7 @@ export default function App({ previewMode = false }) {
|
||||
}, [previewMode, currentPage]);
|
||||
|
||||
const getActiveNavItem = () => {
|
||||
if (currentPage === 'blogs' || currentPage === 'blog') return 'Blogs';
|
||||
if (currentPage === 'contact') return 'Contact Us';
|
||||
if (currentPage === 'about') return 'About Us';
|
||||
switch (activeSection) {
|
||||
@@ -549,8 +589,10 @@ export default function App({ previewMode = false }) {
|
||||
case 'whyus':
|
||||
return 'About Us';
|
||||
case 'products':
|
||||
case 'insights':
|
||||
return 'Products';
|
||||
case 'insights':
|
||||
case 'blogs':
|
||||
return 'Blogs';
|
||||
case 'contactus':
|
||||
return 'Contact Us';
|
||||
default:
|
||||
@@ -582,7 +624,7 @@ export default function App({ previewMode = false }) {
|
||||
};
|
||||
|
||||
const sectionX = isMobile ? '1.25rem' : '4rem';
|
||||
const navItems = ['Home', 'About Us', 'Products', 'Careers', 'Contact Us'];
|
||||
const navItems = ['Home', 'About Us', 'Products', 'Blogs', 'Careers', 'Contact Us'];
|
||||
const NAV_OFFSET = isMobile ? 72 : 88;
|
||||
|
||||
const scrollToSection = (sectionId) => {
|
||||
@@ -600,11 +642,21 @@ export default function App({ previewMode = false }) {
|
||||
const handleNavClick = (item) => {
|
||||
setNavbarVisible(true);
|
||||
setMobileMenuOpen(false);
|
||||
setSelectedBlog(null);
|
||||
|
||||
if (item === 'Careers') {
|
||||
setIsCareersOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item === 'Blogs') {
|
||||
setCurrentPage('blogs');
|
||||
setActiveSection('blogs');
|
||||
window.location.hash = 'blogs';
|
||||
smoothScrollTo(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item === 'Contact Us') {
|
||||
setCurrentPage('contact');
|
||||
setActiveSection('contactus');
|
||||
@@ -645,10 +697,12 @@ export default function App({ previewMode = false }) {
|
||||
if (currentPage !== 'home') {
|
||||
setCurrentPage('home');
|
||||
setActiveSection(id || 'home');
|
||||
window.location.hash = id || 'home';
|
||||
setTimeout(() => doScroll(id), 350);
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.hash = id || 'home';
|
||||
doScroll(id);
|
||||
};
|
||||
|
||||
@@ -891,6 +945,12 @@ export default function App({ previewMode = false }) {
|
||||
<ContactSection isMobile={isMobile} onOpenStrategy={() => setIsStrategyOpen(true)} />
|
||||
) : currentPage === 'about' ? (
|
||||
<AboutUsPage isMobile={isMobile} onOpenStrategy={() => setIsStrategyOpen(true)} onNavigate={handleNavClick} />
|
||||
) : currentPage === 'careers' ? (
|
||||
<CareersPage isMobile={isMobile} onNavigate={handleNavClick} onOpenStrategy={() => setIsStrategyOpen(true)} />
|
||||
) : currentPage === 'blogs' ? (
|
||||
<BlogsPage blogs={insights} isMobile={isMobile} onSelectBlog={handleSelectBlog} onNavigate={handleNavClick} />
|
||||
) : currentPage === 'blog' ? (
|
||||
<BlogDetailPage blog={selectedBlog || insights[0]} allBlogs={insights} isMobile={isMobile} onNavigate={handleNavClick} onSelectBlog={handleSelectBlog} />
|
||||
) : (
|
||||
<>
|
||||
{/* Main Homepage Content */}
|
||||
@@ -1956,8 +2016,29 @@ export default function App({ previewMode = false }) {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Nav Arrows */}
|
||||
<div style={{ display: 'flex', gap: '0.6rem', alignItems: 'center', flexShrink: 0, marginTop: '0.5rem' }}>
|
||||
{/* Nav Arrows & View All Blogs */}
|
||||
<div style={{ display: 'flex', gap: '0.75rem', alignItems: 'center', flexShrink: 0, marginTop: '0.5rem', flexWrap: 'wrap' }}>
|
||||
<button
|
||||
onClick={() => handleNavClick('Blogs')}
|
||||
style={{
|
||||
padding: '0.55rem 1.25rem',
|
||||
borderRadius: '50px',
|
||||
background: 'rgba(56,189,248,0.12)',
|
||||
border: '1px solid rgba(56,189,248,0.3)',
|
||||
color: '#38bdf8',
|
||||
fontWeight: 600,
|
||||
fontSize: '0.85rem',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.4rem',
|
||||
transition: 'all 0.2s ease',
|
||||
}}
|
||||
onMouseEnter={e => { e.currentTarget.style.background = '#38bdf8'; e.currentTarget.style.color = '#000'; }}
|
||||
onMouseLeave={e => { e.currentTarget.style.background = 'rgba(56,189,248,0.12)'; e.currentTarget.style.color = '#38bdf8'; }}
|
||||
>
|
||||
View All Blogs <ArrowRight size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => scrollBlogCarousel(-1)}
|
||||
style={{
|
||||
@@ -2022,6 +2103,7 @@ export default function App({ previewMode = false }) {
|
||||
>
|
||||
<div
|
||||
className="blog-card"
|
||||
onClick={() => handleSelectBlog(insight)}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
|
||||
Reference in New Issue
Block a user