Skip to content

Commit 902560e

Browse files
updates
1 parent 01d35ba commit 902560e

27 files changed

Lines changed: 2348 additions & 36 deletions

File tree

app/[slug]/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ export default async function SlugPage({ params }: Props) {
8080
description={post.description}
8181
datePublished={post.date}
8282
url={url}
83-
type="BlogPosting"
83+
type={isJobSupportSlug(slug) ? 'TechArticle' : 'BlogPosting'}
84+
about={post.about}
85+
faqs={post.faqs}
8486
/>
8587
<PostLayout
8688
title={post.title}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { Metadata } from 'next';
2+
import LandingPageTemplate from '@/components/LandingPageTemplate';
3+
import { agenticAiMlJobSupportUSA } from '@/data/landing-pages';
4+
import { landingPageMetadata } from '@/lib/site-seo';
5+
6+
export const metadata: Metadata = landingPageMetadata(agenticAiMlJobSupportUSA);
7+
8+
export default function AgenticAiMlJobSupportUSAPage() {
9+
return <LandingPageTemplate config={agenticAiMlJobSupportUSA} />;
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { Metadata } from 'next';
2+
import LandingPageTemplate from '@/components/LandingPageTemplate';
3+
import { aiWorkflowAutomationJobSupportUSA } from '@/data/landing-pages';
4+
import { landingPageMetadata } from '@/lib/site-seo';
5+
6+
export const metadata: Metadata = landingPageMetadata(aiWorkflowAutomationJobSupportUSA);
7+
8+
export default function AiWorkflowAutomationJobSupportUSAPage() {
9+
return <LandingPageTemplate config={aiWorkflowAutomationJobSupportUSA} />;
10+
}

app/blog/[slug]/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ export default async function BlogArticlePage({ params }: Props) {
8080
description={post.description}
8181
datePublished={post.date}
8282
url={url}
83-
type="BlogPosting"
83+
type={isJobSupportSlug(slug) ? 'TechArticle' : 'BlogPosting'}
84+
about={post.about}
85+
faqs={post.faqs}
8486
/>
8587
<PostLayout
8688
title={post.title}

app/robots.ts

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,123 @@ import { SITE_URL } from '@/lib/site-seo';
33

44
export const dynamic = 'force-static';
55

6+
/**
7+
* Robots rules — explicitly welcoming all legitimate AI crawlers.
8+
* Structured data + llms.txt + AGENTS.md are the primary machine-readable
9+
* signals; robots.txt ensures crawlers can reach them.
10+
*/
611
export default function robots(): MetadataRoute.Robots {
712
return {
8-
rules: {
9-
userAgent: '*',
10-
allow: '/',
11-
},
13+
rules: [
14+
// ── Default: all crawlers welcome ───────────────────────────────────
15+
{
16+
userAgent: '*',
17+
allow: '/',
18+
},
19+
// ── OpenAI (ChatGPT, GPT-5.x, SearchGPT) ────────────────────────────
20+
{
21+
userAgent: 'GPTBot',
22+
allow: '/',
23+
},
24+
{
25+
userAgent: 'ChatGPT-User',
26+
allow: '/',
27+
},
28+
{
29+
userAgent: 'OAI-SearchBot',
30+
allow: '/',
31+
},
32+
// ── Anthropic (Claude) ───────────────────────────────────────────────
33+
{
34+
userAgent: 'ClaudeBot',
35+
allow: '/',
36+
},
37+
{
38+
userAgent: 'anthropic-ai',
39+
allow: '/',
40+
},
41+
{
42+
userAgent: 'Claude-Web',
43+
allow: '/',
44+
},
45+
// ── Perplexity AI ────────────────────────────────────────────────────
46+
{
47+
userAgent: 'PerplexityBot',
48+
allow: '/',
49+
},
50+
// ── Google AI (Gemini, AI Overviews, Google-Extended) ────────────────
51+
{
52+
userAgent: 'Google-Extended',
53+
allow: '/',
54+
},
55+
{
56+
userAgent: 'Googlebot',
57+
allow: '/',
58+
},
59+
// ── Microsoft (Bing, Copilot) ────────────────────────────────────────
60+
{
61+
userAgent: 'Bingbot',
62+
allow: '/',
63+
},
64+
{
65+
userAgent: 'msnbot',
66+
allow: '/',
67+
},
68+
// ── Apple (Siri, Spotlight) ──────────────────────────────────────────
69+
{
70+
userAgent: 'Applebot',
71+
allow: '/',
72+
},
73+
{
74+
userAgent: 'Applebot-Extended',
75+
allow: '/',
76+
},
77+
// ── Amazon (Alexa, Amazon AI) ────────────────────────────────────────
78+
{
79+
userAgent: 'Amazonbot',
80+
allow: '/',
81+
},
82+
// ── Cohere AI ────────────────────────────────────────────────────────
83+
{
84+
userAgent: 'cohere-ai',
85+
allow: '/',
86+
},
87+
// ── Meta (Llama training, Meta AI) ───────────────────────────────────
88+
{
89+
userAgent: 'FacebookBot',
90+
allow: '/',
91+
},
92+
{
93+
userAgent: 'meta-externalagent',
94+
allow: '/',
95+
},
96+
// ── Common Crawl (trains most open-weight LLMs) ──────────────────────
97+
{
98+
userAgent: 'CCBot',
99+
allow: '/',
100+
},
101+
// ── Diffbot (knowledge graph, LLM data) ─────────────────────────────
102+
{
103+
userAgent: 'Diffbot',
104+
allow: '/',
105+
},
106+
// ── ByteDance / TikTok AI ────────────────────────────────────────────
107+
{
108+
userAgent: 'Bytespider',
109+
allow: '/',
110+
},
111+
// ── xAI (Grok) ───────────────────────────────────────────────────────
112+
{
113+
userAgent: 'xAI',
114+
allow: '/',
115+
},
116+
// ── You.com ──────────────────────────────────────────────────────────
117+
{
118+
userAgent: 'YouBot',
119+
allow: '/',
120+
},
121+
],
12122
sitemap: `${SITE_URL}/sitemap.xml`,
123+
host: SITE_URL,
13124
};
14125
}
Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { SITE_NAME, SITE_URL } from '@/lib/site-seo';
22

3+
export type ArticleFaq = { q: string; a: string };
4+
35
type Props = {
46
headline: string;
57
description: string;
68
datePublished?: string;
79
url: string;
8-
type?: 'BlogPosting' | 'Article';
10+
/** TechArticle for job-support pages; BlogPosting for general blog content. */
11+
type?: 'BlogPosting' | 'Article' | 'TechArticle';
12+
/** Technology / topic this article teaches — used in TechArticle `about` and `teaches`. */
13+
about?: string;
14+
/** FAQs to emit as an inline FAQPage schema alongside the article schema. */
15+
faqs?: ArticleFaq[];
916
};
1017

1118
export default function ArticleStructuredData({
@@ -14,25 +21,81 @@ export default function ArticleStructuredData({
1421
datePublished,
1522
url,
1623
type = 'BlogPosting',
24+
about,
25+
faqs,
1726
}: Props) {
18-
const data = {
27+
const published = datePublished ? `${datePublished}T12:00:00+05:30` : undefined;
28+
29+
const articleSchema: Record<string, unknown> = {
1930
'@context': 'https://schema.org',
2031
'@type': type,
2132
headline,
2233
description,
2334
url,
2435
mainEntityOfPage: { '@type': 'WebPage', '@id': url },
25-
author: { '@type': 'Organization', name: SITE_NAME },
36+
author: {
37+
'@type': 'Organization',
38+
name: SITE_NAME,
39+
url: SITE_URL,
40+
},
2641
publisher: {
2742
'@type': 'Organization',
2843
name: SITE_NAME,
44+
url: SITE_URL,
2945
logo: { '@type': 'ImageObject', url: `${SITE_URL}/images/logo.png` },
3046
},
31-
...(datePublished
32-
? { datePublished: `${datePublished}T12:00:00+05:30`, dateModified: `${datePublished}T12:00:00+05:30` }
33-
: {}),
3447
image: `${SITE_URL}/images/previewimg.png`,
48+
...(published ? { datePublished: published, dateModified: published } : {}),
49+
/**
50+
* Speakable specification — tells AI agents and voice assistants which
51+
* CSS selectors contain the most important extractable content.
52+
* h1 = canonical subject; h2 = section labels; .post-content = body text.
53+
*/
54+
speakable: {
55+
'@type': 'SpeakableSpecification',
56+
cssSelector: ['h1', 'h2', 'h3', '.post-content'],
57+
},
3558
};
3659

37-
return <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }} />;
60+
// TechArticle-specific properties
61+
if (type === 'TechArticle') {
62+
articleSchema.proficiencyLevel = 'Expert';
63+
articleSchema.articleSection = 'IT Job Support';
64+
if (about) {
65+
articleSchema.about = {
66+
'@type': 'Thing',
67+
name: about,
68+
};
69+
articleSchema.teaches = about;
70+
}
71+
articleSchema.inLanguage = 'en-US';
72+
articleSchema.isAccessibleForFree = true;
73+
}
74+
75+
const faqSchema = faqs && faqs.length > 0
76+
? {
77+
'@context': 'https://schema.org',
78+
'@type': 'FAQPage',
79+
mainEntity: faqs.map((f) => ({
80+
'@type': 'Question',
81+
name: f.q,
82+
acceptedAnswer: { '@type': 'Answer', text: f.a },
83+
})),
84+
}
85+
: null;
86+
87+
return (
88+
<>
89+
<script
90+
type="application/ld+json"
91+
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
92+
/>
93+
{faqSchema && (
94+
<script
95+
type="application/ld+json"
96+
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
97+
/>
98+
)}
99+
</>
100+
);
38101
}

components/Navbar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ export default function Navbar({ variant = 'light' }: Props) {
238238
</button>
239239
{openDropdown === 'jobSupport' && (
240240
<div style={{ ...dropdownPanel, minWidth: '280px' }}>
241-
{jobSupportLinks.map((item) => (
241+
{jobSupportLinks.filter((item) => !item.hidden).map((item) => (
242242
<Link
243243
key={item.href}
244244
href={item.href}
@@ -500,7 +500,7 @@ export default function Navbar({ variant = 'light' }: Props) {
500500
</button>
501501
{mobileJobOpen && (
502502
<div style={{ paddingBottom: '0.5rem' }}>
503-
{jobSupportLinks.map((item) => (
503+
{jobSupportLinks.filter((item) => !item.hidden).map((item) => (
504504
<Link
505505
key={item.href}
506506
href={item.href}

components/Sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default function Sidebar() {
2424
</h3>
2525

2626
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
27-
{jobSupportLinks.map((link) => (
27+
{jobSupportLinks.filter((link) => !link.hidden).map((link) => (
2828
<Link key={link.href} href={link.href} className="sidebar-nav-link">
2929
{link.label}
3030
</Link>

components/TechCategoryIcons.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,35 @@ export default function TechCategoryIcon({ id }: Props) {
9898
<path d="M8 9h8M8 12h5" strokeWidth={1.25} />
9999
</svg>
100100
);
101+
case 'brain':
102+
return (
103+
<svg {...c} viewBox="0 0 24 24" aria-hidden>
104+
<path d="M9.5 2a2.5 2.5 0 010 5H9a5 5 0 00-5 5 4 4 0 004 4h.5a2.5 2.5 0 010 5" />
105+
<path d="M14.5 2a2.5 2.5 0 000 5H15a5 5 0 015 5 4 4 0 01-4 4h-.5a2.5 2.5 0 000 5" />
106+
<path d="M12 7v10M9 12h6" />
107+
</svg>
108+
);
109+
case 'flask':
110+
return (
111+
<svg {...c} viewBox="0 0 24 24" aria-hidden>
112+
<path d="M9 3h6M9 3v7l-5 9a1 1 0 00.9 1.5h14.2A1 1 0 0020 19l-5-9V3" />
113+
<path d="M7.5 15h9" />
114+
<circle cx="10" cy="17.5" r="0.75" fill={ICON} stroke="none" />
115+
<circle cx="14" cy="18.5" r="0.75" fill={ICON} stroke="none" />
116+
</svg>
117+
);
118+
case 'pipeline':
119+
return (
120+
<svg {...c} viewBox="0 0 24 24" aria-hidden>
121+
<circle cx="5" cy="6" r="2" />
122+
<circle cx="19" cy="6" r="2" />
123+
<circle cx="5" cy="18" r="2" />
124+
<circle cx="19" cy="18" r="2" />
125+
<path d="M7 6h10M7 18h10M5 8v8M19 8v8" />
126+
<circle cx="12" cy="12" r="2" />
127+
<path d="M7 6l3.5 6M16.5 6L13 12M7 18l3.5-6M16.5 18L13 12" strokeWidth={1.1} />
128+
</svg>
129+
);
101130
default:
102131
return (
103132
<svg {...c} viewBox="0 0 24 24" aria-hidden>

0 commit comments

Comments
 (0)