Resolve merge conflicts — keep stashed improvements (SEO, sitemap)
All checks were successful
Build and Deploy to Storj / build-and-deploy (push) Successful in 10s
All checks were successful
Build and Deploy to Storj / build-and-deploy (push) Successful in 10s
This commit is contained in:
parent
5bd78762d7
commit
a4c14c95ab
2 changed files with 138 additions and 106 deletions
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
interface SEOProps {
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
ogImage?: string;
|
||||
ogType?: string;
|
||||
url?: string;
|
||||
canonicalUrl?: string;
|
||||
pageType?: 'home' | 'speaking' | 'books' | 'consulting' | 'contact' | 'blog';
|
||||
}
|
||||
|
||||
|
|
@ -18,115 +18,150 @@ const {
|
|||
} = Astro.props;
|
||||
|
||||
const siteUrl = 'https://scott.scoble.me';
|
||||
const currentPath = canonicalUrl || Astro.url.pathname;
|
||||
const fullTitle = title.includes('Scott Scoble') ? title : `${title} — Scott Scoble`;
|
||||
const imageUrl = new URL(ogImage, siteUrl).href;
|
||||
const pageUrl = canonicalUrl ? new URL(canonicalUrl, siteUrl).href : `${siteUrl}${Astro.url.pathname}`;
|
||||
const pageUrl = new URL(currentPath, siteUrl).href;
|
||||
|
||||
// Build JSON-LD based on page type
|
||||
const person = {
|
||||
'@type': 'Person',
|
||||
'@id': `${siteUrl}#person`,
|
||||
name: 'Scott Scoble',
|
||||
givenName: 'Scott',
|
||||
familyName: 'Scoble',
|
||||
jobTitle: 'Human Cultivation Expert',
|
||||
url: siteUrl,
|
||||
email: 'mailto:scott@scoble.me',
|
||||
sameAs: [
|
||||
'https://linkedin.com/in/scottscoble',
|
||||
'https://x.com/scottscoble',
|
||||
'https://github.com/sscoble',
|
||||
],
|
||||
};
|
||||
// JSON-LD structured data
|
||||
function getJsonLd(pageType: string): string {
|
||||
const personLd = {
|
||||
'@type': 'Person',
|
||||
'@id': `${siteUrl}#person`,
|
||||
name: 'Scott Scoble',
|
||||
givenName: 'Scott',
|
||||
familyName: 'Scoble',
|
||||
jobTitle: 'Human Cultivation Expert',
|
||||
url: siteUrl,
|
||||
email: 'mailto:scott@scoble.me',
|
||||
sameAs: [
|
||||
'https://linkedin.com/in/scottscoble',
|
||||
'https://x.com/scottscoble',
|
||||
'https://github.com/sscoble',
|
||||
],
|
||||
};
|
||||
|
||||
const website = {
|
||||
'@type': 'WebSite',
|
||||
'@id': `${siteUrl}#website`,
|
||||
name: 'Scott Scoble',
|
||||
description: 'Human Cultivation Expert for the AI Age',
|
||||
url: siteUrl,
|
||||
};
|
||||
switch (pageType) {
|
||||
case 'home': {
|
||||
const websiteLd = {
|
||||
'@type': 'WebSite',
|
||||
'@id': `${siteUrl}#website`,
|
||||
name: 'Scott Scoble',
|
||||
description: 'Human Cultivation Expert for the AI Age',
|
||||
url: siteUrl,
|
||||
};
|
||||
return JSON.stringify([personLd, websiteLd], null, 2);
|
||||
}
|
||||
|
||||
let jsonLd: any;
|
||||
switch (pageType) {
|
||||
case 'home':
|
||||
jsonLd = [person, website];
|
||||
break;
|
||||
case 'speaking':
|
||||
jsonLd = {
|
||||
...person,
|
||||
'@id': `${siteUrl}#speaker`,
|
||||
jobTitle: 'Keynote Speaker',
|
||||
description: 'Human Cultivation Expert speaking on AI, leadership, and team cultivation',
|
||||
};
|
||||
break;
|
||||
case 'books':
|
||||
jsonLd = [
|
||||
{
|
||||
'@type': 'Book',
|
||||
name: 'The Soil Beneath the Code',
|
||||
author: { '@type': 'Person', name: 'Scott Scoble' },
|
||||
datePublished: '2023',
|
||||
bookFormat: 'EBook, Hardcover',
|
||||
},
|
||||
{
|
||||
'@type': 'Book',
|
||||
name: 'A Walk in Mentoring',
|
||||
author: { '@type': 'Person', name: 'Scott Scoble' },
|
||||
datePublished: '2024',
|
||||
bookFormat: 'EBook, Paperback',
|
||||
},
|
||||
];
|
||||
break;
|
||||
case 'consulting':
|
||||
jsonLd = {
|
||||
'@type': 'Service',
|
||||
name: 'AI Team Consulting',
|
||||
description: 'Helping leaders build AI systems that cultivate humans instead of strip-mining them',
|
||||
provider: { '@type': 'Person', name: 'Scott Scoble' },
|
||||
serviceType: 'Professional Consulting',
|
||||
areaServed: 'Worldwide',
|
||||
};
|
||||
break;
|
||||
case 'contact':
|
||||
jsonLd = {
|
||||
'@type': 'ContactPoint',
|
||||
email: 'scott@scoble.me',
|
||||
contactType: 'Customer Service',
|
||||
availableLanguage: 'English',
|
||||
};
|
||||
break;
|
||||
case 'blog':
|
||||
default:
|
||||
jsonLd = {
|
||||
'@type': 'Blog',
|
||||
name: 'Scott Scoble Blog',
|
||||
description: 'Thoughts on AI, leadership, and human cultivation',
|
||||
url: `${siteUrl}/blog`,
|
||||
};
|
||||
break;
|
||||
case 'speaking':
|
||||
return JSON.stringify({
|
||||
'@type': 'Person',
|
||||
'@id': `${siteUrl}#speaker`,
|
||||
name: 'Scott Scoble',
|
||||
jobTitle: 'Keynote Speaker',
|
||||
description: 'Human Cultivation Expert speaking on AI, leadership, and team cultivation',
|
||||
url: siteUrl,
|
||||
sameAs: personLd.sameAs,
|
||||
}, null, 2);
|
||||
|
||||
case 'books':
|
||||
return JSON.stringify([
|
||||
{
|
||||
'@type': 'Book',
|
||||
name: 'The Soil Beneath the Code',
|
||||
author: 'Scott Scoble',
|
||||
datePublished: '2023',
|
||||
bookFormat: 'EBook, Hardcover',
|
||||
},
|
||||
{
|
||||
'@type': 'Book',
|
||||
name: 'A Walk in Mentoring',
|
||||
author: 'Scott Scoble',
|
||||
datePublished: '2024',
|
||||
bookFormat: 'EBook, Paperback',
|
||||
},
|
||||
{
|
||||
'@type': 'Book',
|
||||
name: 'Dark Provenance',
|
||||
author: 'Scott Scoble',
|
||||
datePublished: '2026',
|
||||
bookFormat: 'EBook, Hardcover',
|
||||
},
|
||||
], null, 2);
|
||||
|
||||
case 'consulting':
|
||||
return JSON.stringify({
|
||||
'@type': 'Service',
|
||||
name: 'AI Team Consulting',
|
||||
description: 'Helping leaders build AI systems that cultivate humans instead of strip-mining them',
|
||||
provider: { '@type': 'Person', name: 'Scott Scoble' },
|
||||
serviceType: 'Professional Consulting',
|
||||
areaServed: 'Worldwide',
|
||||
offering: [
|
||||
{
|
||||
'@type': 'Offering',
|
||||
name: 'Keynote Speaking',
|
||||
description: '45-60 minute keynotes on AI, leadership, and team cultivation',
|
||||
},
|
||||
{
|
||||
'@type': 'Offering',
|
||||
name: 'Workshops',
|
||||
description: 'Half-day or full-day workshops on cultivation practices',
|
||||
},
|
||||
],
|
||||
}, null, 2);
|
||||
|
||||
case 'contact':
|
||||
return JSON.stringify({
|
||||
'@type': 'ContactPoint',
|
||||
email: 'scott@scoble.me',
|
||||
contactType: 'Customer Service',
|
||||
availableLanguage: 'English',
|
||||
}, null, 2);
|
||||
|
||||
default:
|
||||
return JSON.stringify({
|
||||
'@type': 'Blog',
|
||||
name: 'Scott Scoble Blog',
|
||||
description: 'Thoughts on AI, leadership, and human cultivation',
|
||||
url: `${siteUrl}/blog`,
|
||||
}, null, 2);
|
||||
}
|
||||
}
|
||||
|
||||
const jsonLd = getJsonLd(pageType);
|
||||
---
|
||||
|
||||
<!-- Primary Meta -->
|
||||
<title>{fullTitle}</title>
|
||||
<meta name="title" content={fullTitle} />
|
||||
<meta name="description" content={description} />
|
||||
<meta name="author" content="Scott Scoble" />
|
||||
<meta name="theme-color" content="#3d5a3e" />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content={fullTitle} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:type" content={ogType} />
|
||||
<meta property="og:url" content={pageUrl} />
|
||||
<meta property="og:image" content={imageUrl} />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:site_name" content="Scott Scoble" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content={fullTitle} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<meta name="twitter:image" content={imageUrl} />
|
||||
<meta name="twitter:site" content="@scottscoble" />
|
||||
|
||||
<!-- Canonical -->
|
||||
<link rel="canonical" href={pageUrl} />
|
||||
|
||||
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
|
||||
<!-- JSON-LD Structured Data -->
|
||||
<script type="application/ld+json" set:html={jsonLd} />
|
||||
|
||||
<!-- PWA / Theme -->
|
||||
<meta name="theme-color" content="#3d5a3e" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
|
|
|||
|
|
@ -1,28 +1,25 @@
|
|||
// Static sitemap for scott.scoble.me
|
||||
// Generated at build time
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const pages = [
|
||||
{ url: '/', changefreq: 'weekly', priority: '1.0' },
|
||||
{ url: '/speaking', changefreq: 'weekly', priority: '0.9' },
|
||||
{ url: '/consulting', changefreq: 'weekly', priority: '0.8' },
|
||||
{ url: '/books', changefreq: 'monthly', priority: '0.8' },
|
||||
{ url: '/about', changefreq: 'monthly', priority: '0.7' },
|
||||
{ url: '/blog', changefreq: 'weekly', priority: '0.7' },
|
||||
{ url: '/contact', changefreq: 'monthly', priority: '0.6' },
|
||||
{ url: '/privacy', changefreq: 'yearly', priority: '0.3' },
|
||||
{ url: '/', lastmod: new Date().toISOString() },
|
||||
{ url: '/speaking', lastmod: new Date().toISOString() },
|
||||
{ url: '/consulting', lastmod: new Date().toISOString() },
|
||||
{ url: '/books', lastmod: new Date().toISOString() },
|
||||
{ url: '/contact', lastmod: new Date().toISOString() },
|
||||
{ url: '/privacy', lastmod: new Date().toISOString() },
|
||||
];
|
||||
|
||||
const siteUrl = 'https://scott.scoble.me';
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
|
||||
// Render the sitemap XML
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
${pages.map(page => ` <url>
|
||||
<loc>${siteUrl}${page.url}</loc>
|
||||
<lastmod>${today}</lastmod>
|
||||
<changefreq>${page.changefreq}</changefreq>
|
||||
<priority>${page.priority}</priority>
|
||||
</url>`).join('\n')}
|
||||
${pages.map(page => `
|
||||
<url>
|
||||
<loc>${new URL(page.url, 'https://scott.scoble.me').href}</loc>
|
||||
<lastmod>${typeof page.lastmod === 'string' ? page.lastmod : new Date(page.lastmod).toISOString().split('T')[0]}</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>${page.url === '/' ? '1.0' : '0.8'}</priority>
|
||||
</url>
|
||||
`).join('')}
|
||||
</urlset>`;
|
||||
|
||||
export const GET = () => {
|
||||
|
|
@ -32,4 +29,4 @@ export const GET = () => {
|
|||
'Cache-Control': 'public, max-age=3600',
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue