The Blog section of Hector Portfolio publishes technical articles focused on SAP, web development, and related technology. The list view atDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/iwinser117/react-portafolio/llms.txt
Use this file to discover all available pages before exploring further.
/blog supports category tabs, tag chips, and free-text search. Clicking any post card navigates to the full article at /blog/:slug, which renders the post’s HTML content, displays metadata, and exposes an inline image injection system using {{img-X}} markers.
Routes
| Path | Component | Description |
|---|---|---|
/blog | Blog.jsx → BlogListModern | Paginated list with category, tag, and search filters |
/blog/:slug | BlogPost.jsx | Single-post full-page view |
Blog.jsx composition
Data Source
Posts are stored insrc/data/posts.json as a local JSON array. BlogService.getAllPosts() reads this file and returns the posts sorted by date descending so the newest article always appears first.
Post Data Shape
Each post object follows this schema (seeEXAMPLE_POST.json):
| Field | Type | Notes |
|---|---|---|
_id | string | Unique identifier |
title | string | Displayed as the card heading and post <h1> |
slug | string | URL segment — must be unique, lowercase, and hyphen-separated |
author | string | Shown in the card footer and post header |
date | string | ISO date (YYYY-MM-DD); used for sorting and display |
category | string | Used for the category tab filter (e.g., "SAP", "React") |
tags | string[] | Shown as #tag chips; first 3 shown in the card, all shown in the post |
excerpt | string | Short summary shown in the list card |
content | string | Full post body as an HTML string; rendered via dangerouslySetInnerHTML |
image | string | string[] | Cover image URL or array of image URLs |
views | number | Incremented automatically by BlogService.incrementViewCount() on post load |
comments | array | Array of comment objects (see Comment shape) |
Inline image markers
Whenimage is an array, the content string may contain {{img-0}}, {{img-1}}, etc. placeholders. BlogPost replaces each marker with a <figure> element containing the corresponding URL from the array:
BlogListModern Features
BlogListModern (src/containers/BlogListModern.jsx) is the main container rendered at /blog. It provides:
- Category tabs — built from
useBlogCategories(). Clicking a tab setsselectedCategory; clicking “Todos” clears it. Each tab shows the post count for that category. - Tag chips — built from
useBlogTags(). Up to 8 tags are shown by default; a +N más button reveals the rest. Selected tags are highlighted and show a✕to deactivate. - Free-text search — filters posts by
titleandexcerptviauseBlogFilter(). A clear button (✕) appears inside the input when a query is present. - Active filters bar — when any filter is active, a summary row shows the active category, tags, and search query with individual remove buttons and a “Limpiar todo” button.
- Pagination — 9 posts per page (
POSTS_PER_PAGE = 9). Previous / Next buttons scroll back to the top on page change. - Contact CTA — a call-to-action section and
<Formulario />are appended after the grid.
BlogPost Features
BlogPost (src/containers/BlogPost.jsx) is rendered at /blog/:slug. It:
- Reads the
slugparam viauseParams(). - Fetches the post with
useBlogPost(slug). - Calls
BlogService.incrementViewCount(slug)in auseEffectonce the post is loaded. - Renders the post header (category, date, views, title, author), the cover image, and the processed HTML
content. - Renders all tags as links to
/blog?tag=<tag>for cross-filtering. - Provides a breadcrumb (
Blog / Post title) and a back link.
Adding a New Blog Post
Add a new post object
Append a new JSON object to the array, following the schema above. Place it at the beginning of the array to have it appear first (posts are sorted descending by
date, so a more recent date also works):Set a unique slug
The
slug becomes the URL path segment at /blog/<slug>. It must be:- unique across all posts
- lowercase
- words separated by hyphens (no spaces or special characters)
"sap-btp-cap-getting-started"Write the content as an HTML string
The
content field is rendered directly as HTML. Supported elements include <h2>, <h3>, <ul>, <ol>, <li>, <p>, <pre>, <strong>, <em>, and <figure>. To embed images inline, set image to an array and use {{img-0}}, {{img-1}}, etc. markers in the content string:Comment Data Shape
Comments are stored as an array nested inside each post object. The shape below reflects whatBlogService.addComment() produces:
| Field | Type | Notes |
|---|---|---|
id | string | Auto-generated as comment-<Date.now()> |
author | string | Required — display name of the commenter |
email | string | null | Optional — set to null if not provided; not displayed publicly |
content | string | Required — must be at least 3 characters |
date | string | ISO date string (YYYY-MM-DD) set at submission time |
approved | boolean | Always false for new comments — moderation required |
BlogService.addComment()
BlogService.addComment() validates the submission before creating the comment object:
authoris required; an empty value throws a validation error.contentis required and must be at least 3 characters long.- The returned object always has
approved: false.
Comments added via
addComment() are not persisted to disk. The JSON data file is static at build time. To enable persistent comments in production, connect BlogService to a real backend API or a hosted database.