Skip to content

Commit be60845

Browse files
RDoc-3634 Add external guides integration
1 parent 93eb954 commit be60845

File tree

10 files changed

+67
-17
lines changed

10 files changed

+67
-17
lines changed

guides/external-guides.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: "Begin analysis with OLAP ETL"
3+
tags: [integration, getting-started]
4+
description: "Learn how RavenDB’s OLAP ETL transforms operational data into Parquet format and ships it to AWS S3 or local storage automatically."
5+
externalUrl: "https://ravendb.net/articles/begin-analysis-with-olap-etl"
6+
publishedAt: 2025-12-23
7+
image: "https://ravendb.net/wp-content/uploads/2025/12/OLAP-article-image.svg"
8+
---
9+
10+
This is an example external guide. When rendered in cards or lists, it will link
11+
directly to the external URL defined in the frontmatter while still participating
12+
in the normal guides and tags system.

guides/home.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import GetStartedGuides from "@site/src/components/Guides/GetStartedGuides";
1515
import TagsGrid from "@site/src/components/Guides/TagsGrid";
1616

1717
<GuidesHeader />
18-
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 lg:gap-4 mb-8">
19-
<div className="lg:col-span-2">
20-
<FeaturedGuides guidesTitles={["Example Guide", "Other Guide"]} />
18+
<div className="grid grid-cols-1 xl:grid-cols-3 gap-8 xl:gap-4 mb-8">
19+
<div className="xl:col-span-2">
20+
<FeaturedGuides guidesTitles={["Begin analysis with OLAP ETL", "Other Guide"]} />
2121
</div>
2222
<RecentGuides />
2323
</div>

src/components/Common/GuideListItem.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Link from "@docusaurus/Link";
33
import clsx from "clsx";
44
import { useTagLimit } from "@site/src/hooks/useTagLimit";
55
import Tag from "@site/src/theme/Tag";
6+
import isInternalUrl from "@docusaurus/isInternalUrl";
67

78
export interface GuideListItemProps {
89
title: string;
@@ -25,7 +26,7 @@ export default function GuideListItem({
2526
<Link
2627
to={url}
2728
className={clsx(
28-
"flex items-center py-3 px-2",
29+
"flex items-center py-3 px-2 gap-4",
2930
"border-b border-black/10 dark:border-white/10 !text-inherit",
3031
"hover:!no-underline",
3132
"group relative",
@@ -72,10 +73,10 @@ export default function GuideListItem({
7273
className={clsx(
7374
"!mb-0 text-xs",
7475
"overflow-hidden text-ellipsis text-right whitespace-nowrap",
75-
"shrink-0",
76-
"w-[92px]",
76+
"shrink-0"
7777
)}
7878
>
79+
{!isInternalUrl(url) && "External • "}
7980
{date}
8081
</p>
8182
)}

src/components/Guides/FeaturedGuides.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default function FeaturedGuides({ guidesTitles }: FeaturedGuidesProps) {
4040
description={guide.description}
4141
imgSrc={guide.image}
4242
imgIcon={guide.icon}
43-
url={guide.permalink}
43+
url={guide.externalUrl || guide.permalink}
4444
tags={guide.tags}
4545
animationDelay={index * 50}
4646
/>

src/components/Guides/RecentGuides.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export default function RecentGuides() {
3838
.filter((doc) => doc.id !== "home")
3939
.map((doc: any) => ({
4040
title: doc.title || doc.id,
41-
url: doc.permalink,
41+
url: doc.externalUrl || doc.permalink,
4242
tags: doc.tags || [],
4343
time: doc.lastUpdatedAt
4444
? getRelativeTime(doc.lastUpdatedAt)
@@ -48,7 +48,7 @@ export default function RecentGuides() {
4848
.slice(0, 10);
4949

5050
return (
51-
<div className="flex flex-col gap-6 lg:h-[0px] lg:min-h-full">
51+
<div className="flex flex-col gap-6 xl:h-[0px] xl:min-h-full">
5252
<div className="flex flex-wrap items-center justify-between shrink-0">
5353
<Heading as="h2" className="!mb-0">
5454
Recent guides

src/components/Guides/RecentGuidesListItem.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Link from "@docusaurus/Link";
33
import Heading from "@theme/Heading";
44
import Tag from "../../theme/Tag";
55
import clsx from "clsx";
6+
import isInternalUrl from "@docusaurus/isInternalUrl";
67
import { useTagLimit } from "@site/src/hooks/useTagLimit";
78

89
export interface RecentGuidesListItemProps {
@@ -68,7 +69,10 @@ export default function RecentGuidesListItem({
6869
</Tag>
6970
)}
7071
</div>
71-
<span className="text-xs shrink-0">{time}</span>
72+
<span className="text-xs shrink-0">
73+
{!isInternalUrl(url) && "External • "}
74+
{time}
75+
</span>
7276
</div>
7377
</div>
7478
);

src/css/custom.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ pre[class*="language-"] {
315315
}
316316

317317
.breadcrumbs__link {
318-
@apply text-gray-600 dark:text-gray-400 no-underline transition-colors leading-[1];
318+
@apply text-gray-600 dark:text-gray-200 no-underline transition-colors leading-[1];
319319
display: inline-flex;
320320
align-items: center;
321321
}

src/pages/guides/all.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ function AllGuidesPageContent(): ReactNode {
127127
description={
128128
guide.description
129129
}
130-
url={guide.permalink}
130+
url={guide.externalUrl || guide.permalink}
131131
imgSrc={guide.image}
132132
imgIcon={guide.icon}
133133
tags={guide.tags}
@@ -175,7 +175,7 @@ function AllGuidesPageContent(): ReactNode {
175175
>
176176
<GuideListItem
177177
title={guide.title}
178-
url={guide.permalink}
178+
url={guide.externalUrl || guide.permalink}
179179
tags={guide.tags}
180180
date={formattedDate}
181181
/>

src/plugins/recent-guides-plugin.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface Guide {
1414
description?: string;
1515
image?: string | { light: string; dark: string };
1616
icon?: IconName;
17+
externalUrl?: string;
1718
}
1819

1920
export interface PluginData {
@@ -93,8 +94,39 @@ const recentGuidesPlugin: Plugin = function recentGuidesPlugin(
9394
: baseName;
9495
const permalink = `/guides/${slug === "index" ? "" : slug}`;
9596

97+
const externalUrl: string | undefined =
98+
(data as any).externalUrl || (data as any).external_url;
99+
100+
const frontmatterDate: unknown = (data as any).publishedAt;
101+
102+
let lastUpdatedAt: number;
103+
if (frontmatterDate) {
104+
let millis: number | null = null;
105+
if (typeof frontmatterDate === "string") {
106+
const parsed = Date.parse(frontmatterDate);
107+
if (!Number.isNaN(parsed)) {
108+
millis = parsed;
109+
}
110+
} else if (frontmatterDate instanceof Date) {
111+
millis = frontmatterDate.getTime();
112+
} else if (typeof frontmatterDate === "number") {
113+
millis =
114+
frontmatterDate > 1e12
115+
? frontmatterDate
116+
: frontmatterDate * 1000;
117+
}
118+
119+
lastUpdatedAt = millis
120+
? Math.floor(millis / 1000)
121+
: Math.floor(stats.mtimeMs / 1000);
122+
} else {
123+
lastUpdatedAt = Math.floor(stats.mtimeMs / 1000);
124+
}
125+
96126
let tags = data.tags || [];
97-
if (!Array.isArray(tags)) {tags = [];}
127+
if (!Array.isArray(tags)) {
128+
tags = [];
129+
}
98130

99131
tags.forEach((tag: string) => {
100132
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
@@ -124,10 +156,11 @@ const recentGuidesPlugin: Plugin = function recentGuidesPlugin(
124156
path.basename(filePath, path.extname(filePath)),
125157
permalink: data.slug || permalink,
126158
tags: formattedTags,
127-
lastUpdatedAt: Math.floor(stats.mtimeMs / 1000),
159+
lastUpdatedAt,
128160
description: data.description,
129161
image: data.image,
130162
icon: data.icon,
163+
externalUrl,
131164
};
132165
});
133166

src/theme/DocTagDocListPage/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ function DocTagDocListPageContent({ tag }: Props): ReactNode {
146146
description={
147147
doc.description
148148
}
149-
url={doc.permalink}
149+
url={guide?.externalUrl || doc.permalink}
150150
imgSrc={guide?.image}
151151
imgIcon={guide?.icon}
152152
tags={guide?.tags}
@@ -199,7 +199,7 @@ function DocTagDocListPageContent({ tag }: Props): ReactNode {
199199
>
200200
<GuideListItem
201201
title={doc.title}
202-
url={doc.permalink}
202+
url={guide?.externalUrl || doc.permalink}
203203
tags={guide?.tags}
204204
date={formattedDate}
205205
/>

0 commit comments

Comments
 (0)