Skip to content

Commit 11e918e

Browse files
committed
fix: docs
1 parent 3479291 commit 11e918e

File tree

2 files changed

+136
-213
lines changed

2 files changed

+136
-213
lines changed

apps/docs/components/sidebar-content.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,21 @@ export const contents: SidebarSection[] = [
128128
href: '/docs/dashboard',
129129
icon: MonitorIcon,
130130
},
131+
],
132+
},
133+
{
134+
title: 'Comparisons',
135+
Icon: ChartLineIcon,
136+
list: [
137+
{
138+
title: 'Databuddy vs Google Analytics',
139+
href: '/docs/comparisons/databuddy-vs-google-analytics',
140+
icon: TrendUpIcon,
141+
},
131142
{
132-
title: 'Comparisons',
133-
href: '/docs/comparisons',
134-
icon: ChartLineIcon,
143+
title: 'Best Analytics Tools 2024',
144+
href: '/docs/comparisons/best-analytics-tools-2024',
145+
icon: ChartBarIcon,
135146
},
136147
],
137148
},

apps/docs/lib/sitemap-generator.ts

Lines changed: 122 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -3,236 +3,148 @@ import { SITE_URL } from '@/app/util/constants';
33
import { getPosts } from '@/lib/blog-query';
44
import { source } from '@/lib/source';
55

6-
// Regex pattern for matching integration pages
7-
const integrationPattern = /\/docs\/integrations\/(.+)/;
8-
9-
// Priority mapping for different page types
10-
const priorityMap: Record<string, number> = {
11-
'/docs': 1.0,
12-
'/docs/getting-started': 0.9,
13-
'/docs/sdk': 0.9,
14-
'/docs/comparisons/databuddy-vs-google-analytics': 0.95,
15-
'/docs/compliance/gdpr-compliance-guide': 0.9,
16-
'/docs/performance/core-web-vitals-guide': 0.85,
17-
'/docs/dashboard': 0.8,
18-
'/docs/security': 0.8,
19-
'/docs/integrations': 0.8,
20-
'/docs/api': 0.7,
21-
'/blog': 0.8,
22-
'/api': 0.7,
23-
'/contributors': 0.8,
24-
'/privacy': 0.5,
25-
'/llms.txt': 0.4,
26-
};
27-
28-
// Change frequency mapping
29-
const changeFrequencyMap: Record<string, 'weekly' | 'monthly' | 'yearly'> = {
30-
'/docs': 'weekly',
31-
'/docs/getting-started': 'weekly',
32-
'/docs/sdk': 'weekly',
33-
'/docs/comparisons/databuddy-vs-google-analytics': 'monthly',
34-
'/docs/compliance/gdpr-compliance-guide': 'monthly',
35-
'/docs/performance/core-web-vitals-guide': 'monthly',
36-
'/docs/dashboard': 'weekly',
37-
'/docs/security': 'monthly',
38-
'/docs/integrations': 'weekly',
39-
'/docs/api': 'monthly',
40-
'/blog': 'monthly',
41-
'/api': 'monthly',
42-
'/contributors': 'monthly',
43-
'/privacy': 'yearly',
44-
'/llms.txt': 'weekly',
45-
};
6+
const priorityRules = [
7+
{ pattern: '/docs', priority: 1.0 },
8+
{ pattern: '/comparisons/databuddy-vs-google-analytics', priority: 0.95 },
9+
{ pattern: '/getting-started', priority: 0.9 },
10+
{ pattern: '/sdk', priority: 0.9 },
11+
{ pattern: '/compliance/gdpr', priority: 0.85 },
12+
{ pattern: '/performance/core-web-vitals', priority: 0.85 },
13+
{ pattern: '/integrations/react', priority: 0.8 },
14+
{ pattern: '/integrations/nextjs', priority: 0.8 },
15+
{ pattern: '/dashboard', priority: 0.8 },
16+
{ pattern: '/security', priority: 0.8 },
17+
{ pattern: '/contributors', priority: 0.8 },
18+
{ pattern: '/pricing', priority: 0.8 },
19+
{ pattern: '/integrations/', priority: 0.7 },
20+
{ pattern: '/blog', priority: 0.7 },
21+
{ pattern: '/api', priority: 0.7 },
22+
{ pattern: '/roadmap', priority: 0.6 },
23+
{ pattern: '/sponsors', priority: 0.6 },
24+
{ pattern: '/ambassadors', priority: 0.6 },
25+
{ pattern: '/privacy', priority: 0.5 },
26+
{ pattern: '/terms', priority: 0.5 },
27+
{ pattern: '/llms.txt', priority: 0.4 },
28+
];
4629

4730
function getPriority(url: string): number {
48-
return priorityMap[url] || (url.includes('/integrations/') ? 0.7 : 0.6);
49-
}
50-
51-
function getChangeFrequency(url: string): 'weekly' | 'monthly' | 'yearly' {
52-
return changeFrequencyMap[url] || 'weekly';
53-
}
54-
55-
function createSitemapEntry(
56-
url: string,
57-
baseUrl: string,
58-
lastModified: Date,
59-
priority?: number,
60-
changeFrequency?: 'weekly' | 'monthly' | 'yearly'
61-
): MetadataRoute.Sitemap[0] {
62-
return {
63-
url: `${baseUrl}${url}`,
64-
lastModified,
65-
changeFrequency: changeFrequency || getChangeFrequency(url),
66-
priority: priority || getPriority(url),
67-
};
68-
}
69-
70-
function processIntegrationPages(entries: MetadataRoute.Sitemap): void {
71-
for (const entry of entries) {
72-
const match = entry.url.match(integrationPattern);
73-
if (match) {
74-
const integrationName = match[1];
75-
if (integrationName === 'react' || integrationName === 'nextjs') {
76-
entry.priority = 0.8;
77-
}
31+
for (const rule of priorityRules) {
32+
if (
33+
url === rule.pattern ||
34+
(rule.pattern !== url && url.includes(rule.pattern))
35+
) {
36+
return rule.priority;
7837
}
7938
}
39+
return 0.6;
8040
}
8141

82-
function processSourcePages(
83-
pages: Array<{ url: string }>,
84-
baseUrl: string,
85-
lastModified: Date
86-
): MetadataRoute.Sitemap {
87-
return pages.map((page) =>
88-
createSitemapEntry(page.url, baseUrl, lastModified)
89-
);
90-
}
91-
92-
function processNonDocPages(
93-
pages: string[],
94-
baseUrl: string,
95-
lastModified: Date
96-
): MetadataRoute.Sitemap {
97-
return pages.map((page) =>
98-
createSitemapEntry(page, baseUrl, lastModified, 0.5, 'yearly')
99-
);
100-
}
101-
102-
function processBlogPages(baseUrl: string): Promise<MetadataRoute.Sitemap> {
103-
return getPosts()
104-
.then((data) => {
105-
if ('error' in data || !data?.posts) {
106-
return [];
107-
}
108-
109-
return data.posts.map((post) => ({
110-
url: `${baseUrl}/blog/${post.slug}`,
111-
lastModified: new Date(post.publishedAt),
112-
changeFrequency: 'monthly' as const,
113-
priority: 0.8,
114-
}));
115-
})
116-
.catch((error) => {
117-
console.warn('Failed to fetch blog posts for sitemap:', error);
118-
return [];
119-
});
120-
}
121-
122-
function getFallbackEntries(): Array<{
123-
url: string;
124-
priority: number;
125-
changeFrequency: 'weekly' | 'monthly' | 'yearly';
126-
}> {
127-
return [
128-
{ url: '/docs', priority: 1.0, changeFrequency: 'weekly' },
129-
{ url: '/docs/getting-started', priority: 0.9, changeFrequency: 'weekly' },
130-
{ url: '/docs/sdk', priority: 0.9, changeFrequency: 'weekly' },
131-
{ url: '/docs/dashboard', priority: 0.8, changeFrequency: 'weekly' },
132-
{ url: '/docs/security', priority: 0.8, changeFrequency: 'monthly' },
133-
{ url: '/docs/api', priority: 0.7, changeFrequency: 'monthly' },
134-
{ url: '/docs/integrations', priority: 0.8, changeFrequency: 'weekly' },
135-
{
136-
url: '/docs/integrations/react',
137-
priority: 0.8,
138-
changeFrequency: 'weekly',
139-
},
140-
{
141-
url: '/docs/integrations/nextjs',
142-
priority: 0.8,
143-
changeFrequency: 'weekly',
144-
},
145-
{
146-
url: '/docs/integrations/wordpress',
147-
priority: 0.8,
148-
changeFrequency: 'weekly',
149-
},
150-
{
151-
url: '/docs/integrations/shopify',
152-
priority: 0.8,
153-
changeFrequency: 'weekly',
154-
},
155-
{
156-
url: '/docs/integrations/stripe',
157-
priority: 0.8,
158-
changeFrequency: 'weekly',
159-
},
160-
{
161-
url: '/docs/integrations/framer',
162-
priority: 0.7,
163-
changeFrequency: 'weekly',
164-
},
165-
{ url: '/docs/integrations/gtm', priority: 0.7, changeFrequency: 'weekly' },
166-
{ url: '/privacy', priority: 0.5, changeFrequency: 'yearly' },
167-
{ url: '/llms.txt', priority: 0.4, changeFrequency: 'weekly' },
168-
];
169-
}
170-
171-
function processFallbackEntries(
172-
baseUrl: string,
173-
lastModified: Date
174-
): MetadataRoute.Sitemap {
175-
const fallbackEntries = getFallbackEntries();
176-
return fallbackEntries.map((entry) =>
177-
createSitemapEntry(
178-
entry.url,
179-
baseUrl,
180-
lastModified,
181-
entry.priority,
182-
entry.changeFrequency
183-
)
184-
);
42+
// Simple change frequency rules
43+
function getChangeFrequency(url: string): 'weekly' | 'monthly' | 'yearly' {
44+
if (url.includes('/privacy') || url.includes('/terms')) {
45+
return 'yearly';
46+
}
47+
if (
48+
url.includes('/compliance/') ||
49+
url.includes('/performance/') ||
50+
url.includes('/security')
51+
) {
52+
return 'monthly';
53+
}
54+
if (url.includes('/api') && !url.includes('/api-keys')) {
55+
return 'monthly';
56+
}
57+
if (url.includes('/blog') && url !== '/blog') {
58+
return 'monthly';
59+
}
60+
if (url.includes('/pricing') || url.includes('/roadmap')) {
61+
return 'monthly';
62+
}
63+
if (
64+
url.includes('/contributors') ||
65+
url.includes('/sponsors') ||
66+
url.includes('/ambassadors')
67+
) {
68+
return 'monthly';
69+
}
70+
return 'weekly';
18571
}
18672

18773
export async function generateSitemapEntries(): Promise<MetadataRoute.Sitemap> {
18874
const lastModified = new Date();
18975
const entries: MetadataRoute.Sitemap = [];
19076

19177
try {
78+
// Get all documentation pages from source
19279
const pages = source.getPages();
193-
const nonDocPages = ['/privacy', '/llms.txt', '/contributors', '/api'];
194-
195-
entries.push(...processSourcePages(pages, SITE_URL, lastModified));
196-
entries.push(...processNonDocPages(nonDocPages, SITE_URL, lastModified));
197-
198-
const blogEntries = await processBlogPages(SITE_URL);
199-
entries.push(...blogEntries);
80+
entries.push(
81+
...pages.map((page) => ({
82+
url: `${SITE_URL}${page.url}`,
83+
lastModified,
84+
changeFrequency: getChangeFrequency(page.url),
85+
priority: getPriority(page.url),
86+
}))
87+
);
20088

201-
// Add the blog index page with lastModified set to latest blog post
202-
const latestBlogModified = blogEntries.length
203-
? new Date(
204-
Math.max(
205-
...blogEntries
206-
.map((e) =>
207-
e.lastModified ? new Date(e.lastModified).getTime() : 0
208-
)
209-
.filter((t) => Number.isFinite(t) && t > 0)
210-
)
211-
)
212-
: lastModified;
89+
// Add static pages that actually exist
90+
const staticPages = [
91+
'/privacy',
92+
'/llms.txt',
93+
'/api',
94+
'/contributors',
95+
'/pricing',
96+
'/roadmap',
97+
'/sponsors',
98+
'/terms',
99+
'/ambassadors',
100+
];
213101
entries.push(
214-
createSitemapEntry(
215-
'/blog',
216-
SITE_URL,
217-
latestBlogModified,
218-
priorityMap['/blog'],
219-
changeFrequencyMap['/blog']
220-
)
102+
...staticPages.map((page) => ({
103+
url: `${SITE_URL}${page}`,
104+
lastModified,
105+
changeFrequency: getChangeFrequency(page),
106+
priority: getPriority(page),
107+
}))
221108
);
222109

223-
processIntegrationPages(entries);
110+
// Add blog posts and blog index
111+
const blogData = await getPosts();
112+
if (!('error' in blogData) && blogData?.posts) {
113+
const blogEntries = blogData.posts.map((post) => ({
114+
url: `${SITE_URL}/blog/${post.slug}`,
115+
lastModified: new Date(post.publishedAt),
116+
changeFrequency: 'monthly' as const,
117+
priority: 0.7,
118+
}));
119+
entries.push(...blogEntries);
120+
121+
// Add blog index with latest post date
122+
const latestPostDate =
123+
blogEntries.length > 0
124+
? blogEntries.reduce(
125+
(latest, entry) =>
126+
entry.lastModified > latest ? entry.lastModified : latest,
127+
blogEntries[0].lastModified
128+
)
129+
: lastModified;
130+
131+
entries.push({
132+
url: `${SITE_URL}/blog`,
133+
lastModified: latestPostDate,
134+
changeFrequency: 'monthly',
135+
priority: 0.8,
136+
});
137+
}
224138
} catch (error) {
225-
console.warn('Failed to generate dynamic sitemap, using fallback:', error);
226-
entries.push(...processFallbackEntries(SITE_URL, lastModified));
139+
console.warn('Sitemap generation failed, using minimal fallback:', error);
140+
// Minimal fallback - just the main docs page
141+
entries.push({
142+
url: `${SITE_URL}/docs`,
143+
lastModified,
144+
changeFrequency: 'weekly',
145+
priority: 1.0,
146+
});
227147
}
228148

229149
return entries;
230150
}
231-
232-
export function getSitemapMetadata() {
233-
return {
234-
title: 'Databuddy Documentation Sitemap',
235-
description:
236-
'Dynamically generated sitemap of Databuddy documentation including all guides, integrations, and API references.',
237-
};
238-
}

0 commit comments

Comments
 (0)