implement blog system with dynamic routing, post content rendering, and SEO configuration
This commit is contained in:
parent
fc07f59865
commit
792a37cb10
BIN
public/blog_can.png
Normal file
BIN
public/blog_can.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
BIN
public/blog_hardware.png
Normal file
BIN
public/blog_hardware.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
BIN
public/blog_track.png
Normal file
BIN
public/blog_track.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
92
src/app/blog/[slug]/PostContent.tsx
Normal file
92
src/app/blog/[slug]/PostContent.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { Calendar, User, Clock, ChevronLeft } from 'lucide-react';
|
||||
import { BlogPost, blogPosts } from '@/lib/blog';
|
||||
import styles from './PostPage.module.css';
|
||||
|
||||
interface Props {
|
||||
post: BlogPost;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
export default function PostContent({ post, slug }: Props) {
|
||||
return (
|
||||
<div className={styles.main}>
|
||||
<div className={styles.hero}>
|
||||
<div className={styles.heroBg}>
|
||||
<Image src={post.img} alt={post.title} fill className={styles.bgImg} priority />
|
||||
<div className={styles.heroOverlay}></div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<motion.div
|
||||
className={styles.heroContent}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<Link href="/blog" className={styles.backBtn}>
|
||||
<ChevronLeft size={16} /> BACK TO HUB
|
||||
</Link>
|
||||
<div className={styles.meta}>
|
||||
<span className={styles.date}><Calendar size={14} /> {post.date}</span>
|
||||
<span className={styles.author}><User size={14} /> {post.author}</span>
|
||||
<span className={styles.readTime}><Clock size={14} /> 5 MIN READ</span>
|
||||
</div>
|
||||
<h1 className={styles.title}>{post.title}</h1>
|
||||
<p className={styles.desc}>{post.desc}</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className={styles.contentSection}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.contentGrid}>
|
||||
<motion.div
|
||||
className={styles.articleBody}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
>
|
||||
<div
|
||||
className={styles.formattedText}
|
||||
dangerouslySetInnerHTML={{ __html: post.content.replace(/\n/g, '<br/>') }}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<aside className={styles.sidebar}>
|
||||
<div className={styles.sidebarBox}>
|
||||
<h3>TECHNICAL SPECS</h3>
|
||||
<div className={styles.specItem}>
|
||||
<span>VERSION</span>
|
||||
<strong>V4.5.6-STABLE</strong>
|
||||
</div>
|
||||
<div className={styles.specItem}>
|
||||
<span>PLATFORM</span>
|
||||
<strong>HONDA K-SERIES</strong>
|
||||
</div>
|
||||
<div className={styles.specItem}>
|
||||
<span>ENCRYPTION</span>
|
||||
<strong>AES-256 BIT</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.relatedBox}>
|
||||
<h3>LATEST UPDATES</h3>
|
||||
{blogPosts.filter(p => p.slug !== slug).map((p, i) => (
|
||||
<Link key={i} href={`/blog/${p.slug}`} className={styles.relatedLink}>
|
||||
{p.title}
|
||||
<ChevronLeft size={12} style={{ transform: 'rotate(180deg)' }} />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,18 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import { useParams } from 'next/navigation';
|
||||
import { blogPosts } from '@/lib/blog';
|
||||
import Navbar from '@/components/Navbar';
|
||||
import Footer from '@/components/Footer';
|
||||
import Image from 'next/image';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Calendar, User, Clock, ChevronLeft } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import PostContent from './PostContent';
|
||||
import styles from './PostPage.module.css';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function BlogPostDetail() {
|
||||
const params = useParams();
|
||||
const slug = params.slug as string;
|
||||
export async function generateStaticParams() {
|
||||
return blogPosts.map((post) => ({
|
||||
slug: post.slug,
|
||||
}));
|
||||
}
|
||||
|
||||
export default async function BlogPostDetail({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params;
|
||||
const post = blogPosts.find((p) => p.slug === slug);
|
||||
|
||||
if (!post) {
|
||||
@ -27,80 +27,7 @@ export default function BlogPostDetail() {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<Navbar />
|
||||
|
||||
<div className={styles.hero}>
|
||||
<div className={styles.heroBg}>
|
||||
<Image src={post.img} alt={post.title} fill className={styles.bgImg} priority />
|
||||
<div className={styles.heroOverlay}></div>
|
||||
</div>
|
||||
|
||||
<div className={styles.container}>
|
||||
<motion.div
|
||||
className={styles.heroContent}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<Link href="/blog" className={styles.backBtn}>
|
||||
<ChevronLeft size={16} /> BACK TO HUB
|
||||
</Link>
|
||||
<div className={styles.meta}>
|
||||
<span className={styles.date}><Calendar size={14} /> {post.date}</span>
|
||||
<span className={styles.author}><User size={14} /> {post.author}</span>
|
||||
<span className={styles.readTime}><Clock size={14} /> 5 MIN READ</span>
|
||||
</div>
|
||||
<h1 className={styles.title}>{post.title}</h1>
|
||||
<p className={styles.desc}>{post.desc}</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className={styles.contentSection}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.contentGrid}>
|
||||
<motion.div
|
||||
className={styles.articleBody}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
>
|
||||
<div
|
||||
className={styles.formattedText}
|
||||
dangerouslySetInnerHTML={{ __html: post.content.replace(/\n/g, '<br/>') }}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<aside className={styles.sidebar}>
|
||||
<div className={styles.sidebarBox}>
|
||||
<h3>TECHNICAL SPECS</h3>
|
||||
<div className={styles.specItem}>
|
||||
<span>VERSION</span>
|
||||
<strong>V4.5.6-STABLE</strong>
|
||||
</div>
|
||||
<div className={styles.specItem}>
|
||||
<span>PLATFORM</span>
|
||||
<strong>HONDA K-SERIES</strong>
|
||||
</div>
|
||||
<div className={styles.specItem}>
|
||||
<span>ENCRYPTION</span>
|
||||
<strong>AES-256 BIT</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.relatedBox}>
|
||||
<h3>LATEST UPDATES</h3>
|
||||
{blogPosts.filter(p => p.slug !== slug).map((p, i) => (
|
||||
<Link key={i} href={`/blog/${p.slug}`} className={styles.relatedLink}>
|
||||
{p.title}
|
||||
<ChevronLeft size={12} style={{ transform: 'rotate(180deg)' }} />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<PostContent post={post} slug={slug} />
|
||||
<Footer />
|
||||
</main>
|
||||
);
|
||||
|
||||
12
src/app/robots.ts
Normal file
12
src/app/robots.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: {
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: '/_next/',
|
||||
},
|
||||
sitemap: 'https://hondavert.com/sitemap.xml',
|
||||
};
|
||||
}
|
||||
33
src/app/sitemap.ts
Normal file
33
src/app/sitemap.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
import { products } from '@/lib/products';
|
||||
import { blogPosts } from '@/lib/blog';
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const baseUrl = 'https://hondavert.com';
|
||||
|
||||
// Core pages
|
||||
const routes = ['', '/about', '/products', '/blog', '/contact'].map((route) => ({
|
||||
url: `${baseUrl}${route}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly' as const,
|
||||
priority: route === '' ? 1 : 0.8,
|
||||
}));
|
||||
|
||||
// Dynamic product pages
|
||||
const productEntries = products.map((product) => ({
|
||||
url: `${baseUrl}/products/${product.id}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly' as const,
|
||||
priority: 0.7,
|
||||
}));
|
||||
|
||||
// Dynamic blog pages
|
||||
const blogEntries = blogPosts.map((post) => ({
|
||||
url: `${baseUrl}/blog/${post.slug}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'weekly' as const,
|
||||
priority: 0.6,
|
||||
}));
|
||||
|
||||
return [...routes, ...productEntries, ...blogEntries];
|
||||
}
|
||||
@ -72,16 +72,19 @@
|
||||
.imageBox {
|
||||
position: relative;
|
||||
height: 250px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.blogImg {
|
||||
object-fit: cover;
|
||||
filter: saturate(0) brightness(0.6);
|
||||
transition: transform 0.5s;
|
||||
filter: saturate(0.2) brightness(0.8);
|
||||
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.card:hover .blogImg {
|
||||
transform: scale(1.05);
|
||||
transform: scale(1.1);
|
||||
filter: saturate(1) brightness(1);
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { ChevronDown, Cpu, Zap, Activity, Gauge, Settings, Menu, X } from 'lucide-react';
|
||||
@ -19,10 +20,14 @@ const icons = {
|
||||
export default function Navbar() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [activeDropdown, setActiveDropdown] = useState<string | null>(null);
|
||||
const pathname = usePathname();
|
||||
|
||||
// Close menu when clicking a link
|
||||
const closeMenu = () => setIsOpen(false);
|
||||
|
||||
// Helper to determine if link is active
|
||||
const isActive = (path: string) => pathname.startsWith(path);
|
||||
|
||||
// Prevent scrolling when menu is open
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
@ -46,9 +51,9 @@ export default function Navbar() {
|
||||
|
||||
{/* Desktop Links */}
|
||||
<div className={styles.links}>
|
||||
<Link href="/about">ABOUT</Link>
|
||||
<Link href="/about" className={pathname === '/about' ? styles.active : ''}>ABOUT</Link>
|
||||
<div className={styles.navItemDropdown}>
|
||||
<Link href="/products" className={styles.active}>
|
||||
<Link href="/products" className={isActive('/products') ? styles.active : ''}>
|
||||
PRODUCTS <ChevronDown size={14} className={styles.chevron} />
|
||||
</Link>
|
||||
<div className={styles.submenu}>
|
||||
@ -67,8 +72,8 @@ export default function Navbar() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/blog">BLOG</Link>
|
||||
<Link href="/contact">CONTACT</Link>
|
||||
<Link href="/blog" className={isActive('/blog') ? styles.active : ''}>BLOG</Link>
|
||||
<Link href="/contact" className={pathname === '/contact' ? styles.active : ''}>CONTACT</Link>
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
|
||||
@ -12,55 +12,66 @@ export const blogPosts: BlogPost[] = [
|
||||
{
|
||||
slug: 'optimizing-k-series-track',
|
||||
title: 'OPTIMIZING THE K-SERIES FOR TRACK USE',
|
||||
desc: 'Technical insights into fuel and ignition management for circuit racing environments.',
|
||||
desc: 'Deep dive into fuel and ignition management strategies specifically designed for sustained high-RPM circuit racing environments.',
|
||||
date: 'MARCH 15, 2026',
|
||||
author: 'MARK H.',
|
||||
img: '/ecu_kpro.png',
|
||||
img: '/blog_track.png',
|
||||
content: `
|
||||
## TRACK-READY CALIBRATION
|
||||
Circuit racing demands more than just peak power. Sustained high-RPM loads and extreme lateral Gs require a calibration strategy focused on reliability and smooth power delivery.
|
||||
Circuit racing demands more than just peak power. Sustained high-RPM loads and extreme lateral Gs require a calibration strategy focused on reliability and smooth, linear power delivery. Unlike drag racing, circuit tuning must prioritize thermal stability over multiple 20-minute sessions.
|
||||
|
||||
### THERMAL MANAGEMENT
|
||||
One of the critical factors we address in our Rev.4 firmware is the intelligent thermal trimming. By monitoring coolant and intake temperatures at 100Hz, our boards can make micro-adjustments to the ignition timing to prevent detonation during late-lap heat soak.
|
||||
### THERMAL MANAGEMENT & IGNITION TRIMMING
|
||||
One of the critical factors we address in our Rev.4 firmware is the intelligent thermal trimming. By monitoring coolant and intake temperatures at 100Hz, our boards can make micro-adjustments to the ignition timing to prevent detonation during late-lap heat soak.
|
||||
|
||||
### STABILITY UNDER LOAD
|
||||
Real-time data logging revealed that voltage drops during hard cornering can affect injector latency. HondaVert's proprietary hardware features improved power conditioning to stabilize these fluctuations, ensuring your AFR stays exactly where it belongs.
|
||||
We recommend a conservative base timing with a progressive advance curve that only reaches peak targets when intake temps are below 35°C. Anything above 50°C triggers a -3 degree safety pull, protecting the valvetrain under harsh conditions.
|
||||
|
||||
### STABILITY UNDER G-LOAD
|
||||
Real-time data logging revealed that voltage drops during hard cornering can affect injector latency and coil dwell time. HondaVert's proprietary hardware features improved power conditioning and higher capacitance to stabilize these fluctuations, ensuring your Air-Fuel Ratio (AFR) stays exactly within the target window, even at 1.5G lateral load.
|
||||
|
||||
### GEAR-DEPENDENT STRATEGY
|
||||
Utilizing the K-Series VSS (Vehicle Speed Sensor) integration, we implement gear-dependent boost and ignition maps. This allows for maximum traction in lower gears while unlocking full performance in 4th and 5th, where aerodynamic drag becomes the primary obstacle.
|
||||
`
|
||||
},
|
||||
{
|
||||
slug: 's300-v3-firmware-update',
|
||||
title: 'S300 V3 FIRMWARE UPDATE RELEASED',
|
||||
desc: 'Exploring the new link stability improvements and telemetry protocols in the latest release.',
|
||||
desc: 'Exploring the new link stability improvements, USB stack rewrite, and enhanced telemetry protocols for the OBD1 platform.',
|
||||
date: 'MARCH 10, 2026',
|
||||
author: 'ADMIN',
|
||||
img: '/hud_telemetry.png',
|
||||
img: '/blog_hardware.png',
|
||||
content: `
|
||||
## S300 EVOLUTION
|
||||
The latest V3 firmware for the S300 Core daughterboard brings professional-grade telemetry to the OBD1 platform.
|
||||
## S300 EVOLUTION: V3 FIRMWARE BREAKTHROUGH
|
||||
The latest V3 firmware for the S300 Core daughterboard brings professional-grade telemetry and connectivity to the veteran OBD1 platform. This update is the result of 18 months of laboratory testing on the OKI/MCU architecture.
|
||||
|
||||
### USB STABILITY
|
||||
We have completely rewritten the USB stack to improve connection speed by 40% when using modern Windows 11 environments. This eliminates the 'COM Port Busy' errors frequently seen with legacy hardware.
|
||||
### USB STACK REWRITE
|
||||
We have completely rewritten the USB communication stack to improve connection speed by 40% when using modern Windows 11 environments. This eliminates the 'COM Port Busy' errors frequently seen with legacy hardware and ensures that live-tuning sessions are never interrupted by driver conflicts.
|
||||
|
||||
### BLUETOOTH LOW LATENCY
|
||||
For Rev.3 board owners, the new firmware optimizes the Bluetooth data stream, allowing for real-time dashboard updates on mobile devices with virtually zero lag. This is critical for monitoring vitals during a drag pass.
|
||||
### BLUETOOTH LOW LATENCY (REV.3 ONLY)
|
||||
For owners of the Rev.3 Bluetooth-enabled boards, the new firmware optimizes the wireless data stream. By utilizing a new packet compression protocol, we've achieved 20ms polling rates for essential vitals. This allows for real-time dashboard updates on mobile devices with virtually zero lag, which is critical for monitoring temperatures during a staging lane idle.
|
||||
|
||||
### ENHANCED DATALOGGING
|
||||
The internal storage buffer has been re-partitioned to support higher resolution logging for 12 essential parameters, including MAP, RPM, TPS, and AFR. You now have access to "High-Res Mode" which logs at 80Hz, providing surgical insight into shift points and throttle transitions.
|
||||
`
|
||||
},
|
||||
{
|
||||
slug: 'precision-tuning-future-flashing',
|
||||
title: 'PRECISION TUNING: THE FUTURE OF FLASHING',
|
||||
desc: 'A look into how CANFlash is changing the speed of development for modern ECU platforms.',
|
||||
desc: 'Understanding how CAN-BUS high-speed communication is revolutionizing ECU development for modern direct-injection Honda engines.',
|
||||
date: 'MARCH 02, 2026',
|
||||
author: 'MARK H.',
|
||||
img: '/engine_bay.png',
|
||||
img: '/blog_can.png',
|
||||
content: `
|
||||
## THE CAN-BUS REVOLUTION
|
||||
Traditional OBDII flashing has been slow and prone to failure if the connection is interrupted. CANFlash utilizes high-speed CAN-FD protocols to increase data throughput by 5x compared to standard K-Line protocols.
|
||||
Traditional OBDII flashing via K-Line (ISO-9141) has been the bottleneck for Honda tuning for a decade. It is slow, prone to electromagnetic interference, and can be dangerous if the connection is interrupted mid-write.
|
||||
|
||||
### ENCRYPTION AND SECURITY
|
||||
Modern Bosch and Keihin ECUs require advanced security handshakes. Our CANFlash interface handles these handshakes locally, reducing the risk of a 'bricked' ECU during the write process.
|
||||
### CAN-FD PROTOCOLS
|
||||
CANFlash utilizes high-speed CAN-FD protocols to increase data throughput by 500% compared to standard protocols. A full base-map write that previously took 4 minutes on a K-Line interface now happens in under 45 seconds.
|
||||
|
||||
### MOBILE DEVELOPMENT
|
||||
With the rise of mobile tuning apps, CANFlash provides a standardized bridge for developers to build safe and fast flashing tools that run directly from a smartphone or tablet.
|
||||
### HARDWARE ENCRYPTION & SECURITY
|
||||
Modern Keihin and Bosch ECUs require advanced security handshakes to avoid "Seed-Key" mismatches. Our CANFlash interface handles these cryptographic handshakes locally on its specialized processor, reducing the risk of a 'bricked' ECU and providing a fail-safe environment for remote tuning sessions.
|
||||
|
||||
### THE MOBILE BRIDGE
|
||||
With the rise of mobile tuning apps, the CANFlash interface provides a standardized, high-speed bridge. It allows developers to build safe flashing tools that run directly from a smartphone or tablet, giving enthusiasts the power to switch maps at the track without lugging a laptop into the cockpit.
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user