From e955236a009dbf79ae692b472dcb8c2e3c04946b Mon Sep 17 00:00:00 2001 From: Paulina Tirante Date: Fri, 15 Aug 2025 13:53:29 -0300 Subject: [PATCH 1/2] EN: adaptar BlogItemDetail + JSON-LD --- src/templates/BlogItemDetail.js | 118 ++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 27 deletions(-) diff --git a/src/templates/BlogItemDetail.js b/src/templates/BlogItemDetail.js index 9137e92..ed92e2f 100644 --- a/src/templates/BlogItemDetail.js +++ b/src/templates/BlogItemDetail.js @@ -2,44 +2,78 @@ import React from "react" import { graphql } from "gatsby" import MarkdownView from "react-showdown" import Layout from "../components/layout" -import { Seo, BannerTop } from "../components/index.js" -import "./BlogItemDetail.scss" +import { Seo, BannerTop, CustomImage } from "../components/index.js" import PropTypes from "prop-types" -import { Helmet } from "react-helmet" -import { getImage, GatsbyImage } from "gatsby-plugin-image" +import { Helmet } from "react-helmet" +import "./BlogItemDetail.scss" + +const stripHtml = s => (s ? s.replace(/<[^>]+>/g, "").trim() : s) +const toISODuration = m => (m ? `PT${Math.max(0, Number(m))}M` : undefined) +const pickHowTo = (blocks = []) => + blocks.find(b => { + const uid = b?.strapi_component || b?.__component + return uid && uid.includes("howto") && b?.tieneHowTo === true + }) const BlogDetail = ({ data }) => { - const { title, description, image, imagePage, author, seo, published_at, updated_at } = + const { title, description, image, imagePage, author, seo, published_at, updated_at, body } = data?.allStrapiArticle?.nodes[0] || {} const bannerTop = imagePage ? { title, imagePage } : { title, image } + const img = imagePage || image; + const imgWidth = img.width || img.localFile.childImageSharp.original.width; + const imgHeight = img.height || img.localFile.childImageSharp.original.height; const structuredData = { "@context": "https://schema.org", - "@type": "Article", - "headline": seo?.pageTitle || title, // Usa pageTitle de SEO o el título + "@type": "BlogPosting", + "headline": seo?.pageTitle || title, "description": seo?.pageDescription || description, - "image": imagePage?.url || image?.url, // Imagen principal + "image": { + "@type": "ImageObject", + "url": img.url, + "width": imgWidth, + "height": imgHeight + }, "author": author?.map(auth => ({ "@type": "Person", "name": auth.name, })), - "datePublished": published_at, // Ajusta con la fecha real - "dateModified": updated_at, // Ajusta con la fecha real + "datePublished": published_at, + "dateModified": updated_at, "mainEntityOfPage": { "@type": "WebPage", - "@id": `https://es.bitlogic.io/blog/${data?.allStrapiArticle?.nodes[0]?.slug}`, + "@id": `https://en.bitlogic.io/blog/${data?.allStrapiArticle?.nodes[0]?.slug}`, // <-- EN }, "publisher": { "@type": "Organization", "name": "Bitlogic", "logo": { "@type": "ImageObject", - "url": "https://bitlogic.io/static/64f396cb88cfcbfda46b86c5218242f2/de081/Logo_Bit_azul_7e725e9726.webp", // URL del logo del sitio - }, + "url": "https://bitlogic.io/static/64f396cb88cfcbfda46b86c5218242f2/de081/Logo_Bit_azul_7e725e9726.webp", + "width": 633, + "height": 187 + } }, } + const blocks = Array.isArray(body) ? body : [] + const howto = pickHowTo(blocks) + + const structuredHowTo = howto && { + "@context": "https://schema.org", + "@type": "HowTo", + name: howto.title || title, + description: stripHtml(howto.descripcion || howto.description), + totalTime: toISODuration(howto.totalMinutes), + tool: (howto.tools || []).map(t => t?.name).filter(Boolean), + step: (howto.steps || []).map(s => ({ + "@type": "HowToStep", + name: s?.name, + text: stripHtml(s?.text), + })), + } + return ( { {JSON.stringify(structuredData)} + {structuredHowTo && ( + + + + )} +
@@ -62,15 +104,14 @@ const BlogDetail = ({ data }) => { />
{author?.map(author => ( -
- {author.image && ( -
- -
- )} +
+
+ +
{author?.name}
{author?.subTitle}
@@ -94,6 +135,12 @@ BlogDetail.propTypes = { title: PropTypes.string.isRequired, description: PropTypes.string.isRequired, slug: PropTypes.string.isRequired, + body: PropTypes.array, + seo: PropTypes.shape({ + pageTitle: PropTypes.string, + pageDescription: PropTypes.string, + pageKeywords: PropTypes.string, + }), image: PropTypes.shape({ url: PropTypes.string.isRequired, alternativeText: PropTypes.string, @@ -104,7 +151,7 @@ BlogDetail.propTypes = { }), }), imagePage: PropTypes.shape({ - url: PropTypes.string, + url: PropTypes.string.isRequired, alternativeText: PropTypes.string, localFile: PropTypes.shape({ childImageSharp: PropTypes.shape({ @@ -114,12 +161,11 @@ BlogDetail.propTypes = { }), author: PropTypes.arrayOf( PropTypes.shape({ - id: PropTypes.number.isRequired, name: PropTypes.string.isRequired, subTitle: PropTypes.string, summary: PropTypes.string, image: PropTypes.shape({ - url: PropTypes.string, + url: PropTypes.string.isRequired, alternativeText: PropTypes.string, localFile: PropTypes.shape({ childImageSharp: PropTypes.shape({ @@ -137,7 +183,7 @@ BlogDetail.propTypes = { export const query = graphql` query($slug: String!) { - allStrapiArticle: allStrapiEnglishArticle(filter: { slug: { eq: $slug } }) { + allStrapiArticle: allStrapiEnglishArticle(filter: { slug: { eq: $slug } }) { # <-- alias mantiene el mismo nombre en props nodes { title description @@ -145,6 +191,17 @@ export const query = graphql` published_at updated_at destacado + body { + ... on ComponentHowtoHowTo { + id + title + descripcion + totalMinutes + tieneHowTo + tools { name } + steps { name text } + } + } seo { pageTitle pageDescription @@ -153,26 +210,33 @@ export const query = graphql` image { url alternativeText + width + height localFile { childImageSharp { gatsbyImageData + original { width height } } } } imagePage { + url alternativeText + width + height localFile { childImageSharp { gatsbyImageData + original { width height } } } } author { - id name subTitle summary image { + url alternativeText localFile { childImageSharp { From f14b323730009a65ff7cd1d180cf00aeadeb505a Mon Sep 17 00:00:00 2001 From: Bruno Portesio Date: Fri, 15 Aug 2025 14:39:11 -0300 Subject: [PATCH 2/2] update graphql definitions --- src/schema/blogSchema.js | 3 +++ src/schema/generalSchema.js | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/schema/blogSchema.js b/src/schema/blogSchema.js index a93f224..bf0d5aa 100644 --- a/src/schema/blogSchema.js +++ b/src/schema/blogSchema.js @@ -1,4 +1,6 @@ const blogSchema = ` + union ArticleBodyDynamicZone = ComponentHowtoHowTo + type StrapiEnglishBlogCategory implements Node { parent: Node children: [Node!]! @@ -42,6 +44,7 @@ const blogSchema = ` seo: ComponentSeo blog_category: StrapiEnglishBlogCategory author: [StrapiEnglishArticleAuthor] + body: [ArticleBodyDynamicZone] published_at( formatString: String fromNow: Boolean diff --git a/src/schema/generalSchema.js b/src/schema/generalSchema.js index 8a68b42..57ab57b 100644 --- a/src/schema/generalSchema.js +++ b/src/schema/generalSchema.js @@ -58,6 +58,7 @@ const generalSchema = ` summary: String text: String description: String + descripcion: String concactFormAnchor: String callToAction: String videoUrl: String @@ -72,6 +73,8 @@ const generalSchema = ` contactForm: Boolean allBlog: Boolean show: Boolean + tieneHowTo: Boolean + totalMinutes: Int image: LocalFile imageDark: LocalFile backgroundImage: LocalFile @@ -86,6 +89,8 @@ const generalSchema = ` ListItem: [ComponentContentPageCard] eng_professionals: [StrapiEnglishProfessional] english_articles: [StrapiEnglishArticle] + tools: [ComponentHowtoTool] + steps: [ComponentHowtoStep] } type ComponentSeo { @@ -94,6 +99,25 @@ const generalSchema = ` pageKeywords: String } + type ComponentHowtoHowTo { + id: ID! + tieneHowTo: Boolean + title: String + descripcion: String + totalMinutes: Int + tools: [ComponentHowtoTool] + steps: [ComponentHowtoStep] + } + type ComponentHowtoStep { + id: ID! + name: String + text: String + } + type ComponentHowtoTool { + id: ID! + name: String + } + type ComponentButton { id: Int content: String