@fullstackdatasolutions/articles 0.12.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +559 -11
- package/dist/index.cjs +984 -388
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +383 -21
- package/dist/index.d.ts +383 -21
- package/dist/index.js +968 -376
- package/dist/index.js.map +1 -1
- package/dist/nextjs.cjs +74 -6
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +149 -0
- package/dist/nextjs.d.ts +149 -0
- package/dist/nextjs.js +74 -6
- package/dist/nextjs.js.map +1 -1
- package/dist/server.cjs +665 -27
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +357 -3
- package/dist/server.d.ts +357 -3
- package/dist/server.js +643 -29
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/ArticleCard.tsx +42 -6
- package/src/ArticleContent.tsx +144 -5
- package/src/ArticleDetailHero.tsx +33 -1
- package/src/ArticleNavigation.tsx +32 -1
- package/src/ArticleSchemas.tsx +43 -39
- package/src/ArticleSocialShare.tsx +54 -10
- package/src/ArticlesPage.tsx +56 -5
- package/src/AuthorArticlesPage.tsx +308 -14
- package/src/AuthorCard.tsx +1 -1
- package/src/CategoryArticlesPage.tsx +34 -2
- package/src/LatestArticles.tsx +28 -1
- package/src/LatestArticlesSection.tsx +15 -1
- package/src/PaginationNav.tsx +78 -0
- package/src/RelatedArticlesSection.tsx +58 -0
- package/src/SeriesArticlesPage.tsx +66 -0
- package/src/__tests__/ArticleCard.test.tsx +63 -3
- package/src/__tests__/ArticleContent.test.tsx +143 -0
- package/src/__tests__/ArticleDetailHero.test.tsx +30 -0
- package/src/__tests__/ArticleNavigation.test.tsx +81 -3
- package/src/__tests__/ArticleSchemas.test.tsx +155 -81
- package/src/__tests__/ArticleSocialShare.test.tsx +54 -0
- package/src/__tests__/ArticlesPage.test.tsx +131 -0
- package/src/__tests__/AuthorArticlesPage.test.tsx +304 -3
- package/src/__tests__/CategoryArticlesPage.test.tsx +116 -1
- package/src/__tests__/LatestArticles.test.tsx +52 -0
- package/src/__tests__/LatestArticlesSection.test.tsx +28 -0
- package/src/__tests__/PaginationNav.test.tsx +73 -0
- package/src/__tests__/RelatedArticlesSection.test.tsx +132 -0
- package/src/__tests__/SeriesArticlesPage.test.tsx +121 -0
- package/src/__tests__/eventTracking.test.tsx +145 -0
- package/src/__tests__/events.test.ts +82 -0
- package/src/__tests__/markdown.test.ts +78 -1
- package/src/__tests__/pagination.test.ts +178 -0
- package/src/__tests__/seoUtils-authors.test.ts +37 -0
- package/src/__tests__/seoUtils.test.ts +246 -0
- package/src/__tests__/server-articles.test.ts +356 -1
- package/src/__tests__/validateArticles.test.ts +312 -0
- package/src/articleTypes.ts +109 -0
- package/src/articlesConfig.ts +45 -1
- package/src/eventTracking.tsx +97 -0
- package/src/events.ts +105 -0
- package/src/index.ts +26 -1
- package/src/markdown.ts +41 -0
- package/src/pagination.ts +93 -0
- package/src/seoUtils.ts +198 -11
- package/src/server-articles.ts +199 -6
- package/src/server.ts +46 -2
- package/src/validateArticles.ts +260 -0
package/dist/index.cjs
CHANGED
|
@@ -75,10 +75,10 @@ __export(src_exports, {
|
|
|
75
75
|
ArticleDetailHero: () => ArticleDetailHero,
|
|
76
76
|
ArticleNavigation: () => ArticleNavigation,
|
|
77
77
|
ArticleSEO: () => ArticleSEO,
|
|
78
|
-
ArticleSchema: () => ArticleSchema,
|
|
79
78
|
ArticleSearchBar: () => ArticleSearchBar,
|
|
80
79
|
ArticleSocialShare: () => ArticleSocialShare,
|
|
81
80
|
ArticleTOC: () => ArticleTOC,
|
|
81
|
+
ArticleViewTracker: () => ArticleViewTracker,
|
|
82
82
|
ArticlesHero: () => ArticlesHero,
|
|
83
83
|
ArticlesPage: () => ArticlesPage,
|
|
84
84
|
AuthorArticlesPage: () => AuthorArticlesPage,
|
|
@@ -93,6 +93,7 @@ __export(src_exports, {
|
|
|
93
93
|
CommentItem: () => CommentItem,
|
|
94
94
|
CommentThread: () => CommentThread,
|
|
95
95
|
CommentsSection: () => CommentsSection,
|
|
96
|
+
CtaViewTracker: () => CtaViewTracker,
|
|
96
97
|
DEFAULT_CATEGORIES_PAGE_SIZE: () => DEFAULT_CATEGORIES_PAGE_SIZE,
|
|
97
98
|
DEFAULT_LAYOUT: () => DEFAULT_LAYOUT,
|
|
98
99
|
DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
|
|
@@ -100,7 +101,10 @@ __export(src_exports, {
|
|
|
100
101
|
FeaturedArticle: () => FeaturedArticle,
|
|
101
102
|
LatestArticles: () => LatestArticles,
|
|
102
103
|
LatestArticlesSection: () => LatestArticlesSection,
|
|
104
|
+
PaginationNav: () => PaginationNav,
|
|
105
|
+
RelatedArticlesSection: () => RelatedArticlesSection,
|
|
103
106
|
ScrollToTop: () => ScrollToTop,
|
|
107
|
+
SeriesArticlesPage: () => SeriesArticlesPage,
|
|
104
108
|
breadcrumbsAreEnabled: () => breadcrumbsAreEnabled,
|
|
105
109
|
getBreadcrumbsConfig: () => getBreadcrumbsConfig,
|
|
106
110
|
useArticles: () => useArticles
|
|
@@ -297,43 +301,17 @@ function BreadcrumbSchema({ items }) {
|
|
|
297
301
|
|
|
298
302
|
// src/ArticleSchemas.tsx
|
|
299
303
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
300
|
-
function getPersonSchemas(article, authors) {
|
|
304
|
+
function getPersonSchemas(article, authors, config) {
|
|
301
305
|
let resolvedAuthors = [];
|
|
302
306
|
if (authors == null ? void 0 : authors.length) {
|
|
303
307
|
resolvedAuthors = [...authors];
|
|
304
308
|
} else if (article.author) {
|
|
305
309
|
resolvedAuthors = [{ name: article.author, slug: "", bio: "" }];
|
|
306
310
|
}
|
|
307
|
-
return resolvedAuthors.map((author) => __spreadValues(__spreadValues({
|
|
311
|
+
return resolvedAuthors.map((author) => __spreadValues(__spreadValues(__spreadValues({
|
|
308
312
|
"@type": "Person",
|
|
309
313
|
name: author.name
|
|
310
|
-
}, author.url && { url: author.url }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }));
|
|
311
|
-
}
|
|
312
|
-
function ArticleSchema({
|
|
313
|
-
article,
|
|
314
|
-
articleUrl,
|
|
315
|
-
siteName,
|
|
316
|
-
showAuthor = true,
|
|
317
|
-
authors
|
|
318
|
-
}) {
|
|
319
|
-
const personSchemas = getPersonSchemas(article, authors);
|
|
320
|
-
const schema = __spreadProps(__spreadValues(__spreadValues({
|
|
321
|
-
"@context": "https://schema.org",
|
|
322
|
-
"@type": "Article",
|
|
323
|
-
headline: article.title,
|
|
324
|
-
description: article.excerpt,
|
|
325
|
-
image: article.featuredImage
|
|
326
|
-
}, article.date && { datePublished: new Date(article.date).toISOString() }), showAuthor && personSchemas.length > 0 && { author: personSchemas }), {
|
|
327
|
-
publisher: { "@type": "Organization", name: siteName },
|
|
328
|
-
mainEntityOfPage: { "@type": "WebPage", "@id": articleUrl }
|
|
329
|
-
});
|
|
330
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
331
|
-
"script",
|
|
332
|
-
{
|
|
333
|
-
type: "application/ld+json",
|
|
334
|
-
dangerouslySetInnerHTML: { __html: JSON.stringify(schema) }
|
|
335
|
-
}
|
|
336
|
-
);
|
|
314
|
+
}, author.url && { url: author.url }), getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }));
|
|
337
315
|
}
|
|
338
316
|
function ArticleSEO({
|
|
339
317
|
article,
|
|
@@ -341,12 +319,13 @@ function ArticleSEO({
|
|
|
341
319
|
siteName,
|
|
342
320
|
siteLogo,
|
|
343
321
|
showAuthor = true,
|
|
344
|
-
authors
|
|
322
|
+
authors,
|
|
323
|
+
config
|
|
345
324
|
}) {
|
|
346
325
|
const publishedAt = article.date ? new Date(article.date).toISOString() : void 0;
|
|
347
326
|
const modifiedAt = article.lastmod ? new Date(article.lastmod).toISOString() : publishedAt;
|
|
348
|
-
const personSchemas = getPersonSchemas(article, authors);
|
|
349
|
-
const structuredData = __spreadValues(__spreadProps(__spreadValues(__spreadValues(__spreadValues({
|
|
327
|
+
const personSchemas = getPersonSchemas(article, authors, config);
|
|
328
|
+
const structuredData = __spreadValues(__spreadValues(__spreadValues(__spreadProps(__spreadValues(__spreadValues(__spreadValues({
|
|
350
329
|
"@context": "https://schema.org",
|
|
351
330
|
"@type": article.articleType || "Article",
|
|
352
331
|
mainEntityOfPage: { "@type": "WebPage", "@id": articleUrl },
|
|
@@ -357,8 +336,9 @@ function ArticleSEO({
|
|
|
357
336
|
"@type": "Organization",
|
|
358
337
|
name: siteName
|
|
359
338
|
}, siteLogo && { logo: { "@type": "ImageObject", url: siteLogo } }),
|
|
360
|
-
description: article.excerpt
|
|
361
|
-
|
|
339
|
+
description: article.excerpt,
|
|
340
|
+
articleSection: article.category
|
|
341
|
+
}), article.tags && article.tags.length > 0 && { keywords: article.tags.join(", ") }), article.wordCount !== void 0 && { wordCount: article.wordCount }), article.series && { isPartOf: { "@type": "Blog", name: article.series } });
|
|
362
342
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
363
343
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
364
344
|
"script",
|
|
@@ -426,16 +406,27 @@ function CollectionPageSchema({
|
|
|
426
406
|
title,
|
|
427
407
|
description,
|
|
428
408
|
url,
|
|
429
|
-
articleCount
|
|
409
|
+
articleCount,
|
|
410
|
+
items
|
|
430
411
|
}) {
|
|
431
|
-
const schema = {
|
|
412
|
+
const schema = __spreadValues({
|
|
432
413
|
"@context": "https://schema.org",
|
|
433
414
|
"@type": "CollectionPage",
|
|
434
415
|
name: title,
|
|
435
416
|
description,
|
|
436
417
|
url,
|
|
437
418
|
numberOfItems: articleCount
|
|
438
|
-
}
|
|
419
|
+
}, items && items.length > 0 && {
|
|
420
|
+
mainEntity: {
|
|
421
|
+
"@type": "ItemList",
|
|
422
|
+
itemListElement: items.map((item) => ({
|
|
423
|
+
"@type": "ListItem",
|
|
424
|
+
position: item.position,
|
|
425
|
+
url: item.url,
|
|
426
|
+
name: item.name
|
|
427
|
+
}))
|
|
428
|
+
}
|
|
429
|
+
});
|
|
439
430
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
440
431
|
"script",
|
|
441
432
|
{
|
|
@@ -570,20 +561,99 @@ function FeaturedArticle({ article, showAuthor = true }) {
|
|
|
570
561
|
}
|
|
571
562
|
|
|
572
563
|
// src/LatestArticlesSection.tsx
|
|
573
|
-
var
|
|
564
|
+
var import_lucide_react6 = require("lucide-react");
|
|
574
565
|
|
|
575
566
|
// src/LatestArticles.tsx
|
|
576
567
|
var import_react2 = require("react");
|
|
577
568
|
|
|
578
569
|
// src/ArticleCard.tsx
|
|
570
|
+
var import_image4 = __toESM(require("next/image"), 1);
|
|
571
|
+
var import_link6 = __toESM(require("next/link"), 1);
|
|
572
|
+
var import_lucide_react4 = require("lucide-react");
|
|
573
|
+
|
|
574
|
+
// src/AuthorCard.tsx
|
|
579
575
|
var import_image3 = __toESM(require("next/image"), 1);
|
|
580
576
|
var import_link5 = __toESM(require("next/link"), 1);
|
|
581
577
|
var import_lucide_react3 = require("lucide-react");
|
|
582
578
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
583
|
-
function
|
|
584
|
-
return
|
|
585
|
-
|
|
579
|
+
function getInitials(name) {
|
|
580
|
+
return name.split(" ").filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
|
|
581
|
+
}
|
|
582
|
+
function AuthorCard({
|
|
583
|
+
author,
|
|
584
|
+
linkToPage = true,
|
|
585
|
+
showBio = false,
|
|
586
|
+
showSocial = false
|
|
587
|
+
}) {
|
|
588
|
+
const avatar = getAuthorAvatar(author);
|
|
589
|
+
const name = linkToPage ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_link5.default, { href: getAuthorUrl(author), className: "font-medium text-foreground hover:text-primary", children: author.name }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-medium text-foreground", children: author.name });
|
|
590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-start gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground", children: [
|
|
591
|
+
avatar ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
586
592
|
import_image3.default,
|
|
593
|
+
{
|
|
594
|
+
src: avatar,
|
|
595
|
+
alt: author.name,
|
|
596
|
+
width: 48,
|
|
597
|
+
height: 48,
|
|
598
|
+
className: "h-12 w-12 rounded-full object-cover"
|
|
599
|
+
}
|
|
600
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-semibold text-muted-foreground", children: getInitials(author.name) }),
|
|
601
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
602
|
+
name,
|
|
603
|
+
showBio && author.bio && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mt-1 line-clamp-2 text-sm text-muted-foreground", children: author.bio }),
|
|
604
|
+
showSocial && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AuthorSocialLinks, { author })
|
|
605
|
+
] })
|
|
606
|
+
] });
|
|
607
|
+
}
|
|
608
|
+
var SOCIAL_ICONS = {
|
|
609
|
+
Website: import_lucide_react3.Globe,
|
|
610
|
+
Facebook: import_lucide_react3.Facebook,
|
|
611
|
+
Twitter: import_lucide_react3.Twitter,
|
|
612
|
+
X: import_lucide_react3.Twitter,
|
|
613
|
+
LinkedIn: import_lucide_react3.Linkedin,
|
|
614
|
+
Instagram: import_lucide_react3.Instagram,
|
|
615
|
+
YouTube: import_lucide_react3.Youtube,
|
|
616
|
+
GitHub: import_lucide_react3.Github,
|
|
617
|
+
Newsletter: import_lucide_react3.Mail
|
|
618
|
+
};
|
|
619
|
+
function getSocialIcon(label) {
|
|
620
|
+
var _a;
|
|
621
|
+
return (_a = SOCIAL_ICONS[label]) != null ? _a : import_lucide_react3.MessageCircle;
|
|
622
|
+
}
|
|
623
|
+
function AuthorSocialLinks({ author }) {
|
|
624
|
+
const links = getAuthorSocialLinks(author);
|
|
625
|
+
if (links.length === 0) return null;
|
|
626
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-3 flex items-center gap-2", children: links.map(({ href, label }) => {
|
|
627
|
+
const Icon = getSocialIcon(label);
|
|
628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
629
|
+
import_link5.default,
|
|
630
|
+
{
|
|
631
|
+
href,
|
|
632
|
+
className: "inline-flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
633
|
+
"aria-label": `${author.name} on ${label}`,
|
|
634
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { className: "h-4 w-4" })
|
|
635
|
+
},
|
|
636
|
+
`${label}-${href}`
|
|
637
|
+
);
|
|
638
|
+
}) });
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/events.ts
|
|
642
|
+
function emitArticleEvent(handler, event) {
|
|
643
|
+
if (!handler) return;
|
|
644
|
+
try {
|
|
645
|
+
handler(__spreadProps(__spreadValues({}, event), { timestamp: Date.now() }));
|
|
646
|
+
} catch (e) {
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/ArticleCard.tsx
|
|
651
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
652
|
+
function ArticleCard({ article, config }) {
|
|
653
|
+
var _a;
|
|
654
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rounded-lg border border-border bg-card text-card-foreground shadow-sm hover:shadow-lg transition-shadow duration-300", children: [
|
|
655
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
656
|
+
import_image4.default,
|
|
587
657
|
{
|
|
588
658
|
src: article.featuredImage,
|
|
589
659
|
alt: article.title,
|
|
@@ -592,35 +662,67 @@ function ArticleCard({ article }) {
|
|
|
592
662
|
height: 200
|
|
593
663
|
}
|
|
594
664
|
) }),
|
|
595
|
-
/* @__PURE__ */ (0,
|
|
596
|
-
/* @__PURE__ */ (0,
|
|
597
|
-
/* @__PURE__ */ (0,
|
|
598
|
-
|
|
665
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-6", children: [
|
|
666
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex gap-2 mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "bg-primary/10 text-primary text-xs font-medium px-2 py-1 rounded", children: article.category }) }),
|
|
667
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
668
|
+
"h3",
|
|
599
669
|
{
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
children: article.title
|
|
670
|
+
className: "text-lg font-semibold leading-none tracking-tight mb-2",
|
|
671
|
+
style: { fontFamily: (_a = config == null ? void 0 : config.theme) == null ? void 0 : _a.headerFontFamily },
|
|
672
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_link6.default, { href: `/articles/${article.slug}`, className: "hover:text-primary transition-colors", children: article.title })
|
|
603
673
|
}
|
|
604
|
-
)
|
|
605
|
-
/* @__PURE__ */ (0,
|
|
606
|
-
/* @__PURE__ */ (0,
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
674
|
+
),
|
|
675
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-muted-foreground mb-4 line-clamp-3", children: article.excerpt }),
|
|
676
|
+
article.authorSlug && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
677
|
+
import_link6.default,
|
|
678
|
+
{
|
|
679
|
+
href: `/articles/authors/${article.authorSlug}`,
|
|
680
|
+
className: "mb-4 flex items-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors",
|
|
681
|
+
onClick: () => emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
682
|
+
name: "author_clicked",
|
|
683
|
+
articleSlug: article.slug,
|
|
684
|
+
authorSlug: article.authorSlug
|
|
685
|
+
}),
|
|
686
|
+
children: [
|
|
687
|
+
article.authorAvatar ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
688
|
+
import_image4.default,
|
|
689
|
+
{
|
|
690
|
+
src: article.authorAvatar,
|
|
691
|
+
alt: "",
|
|
692
|
+
width: 24,
|
|
693
|
+
height: 24,
|
|
694
|
+
className: "h-6 w-6 rounded-full object-cover"
|
|
695
|
+
}
|
|
696
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
697
|
+
"span",
|
|
698
|
+
{
|
|
699
|
+
"aria-hidden": "true",
|
|
700
|
+
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground",
|
|
701
|
+
children: getInitials(article.author)
|
|
702
|
+
}
|
|
703
|
+
),
|
|
704
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: article.author })
|
|
705
|
+
]
|
|
706
|
+
}
|
|
707
|
+
),
|
|
708
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between text-sm text-muted-foreground border-t pt-4", children: [
|
|
709
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
710
|
+
article.date && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
711
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Calendar, { className: "h-3 w-3" }),
|
|
712
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: article.date })
|
|
611
713
|
] }),
|
|
612
|
-
/* @__PURE__ */ (0,
|
|
613
|
-
/* @__PURE__ */ (0,
|
|
614
|
-
/* @__PURE__ */ (0,
|
|
714
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
715
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Clock, { className: "h-3 w-3" }),
|
|
716
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: article.readTime })
|
|
615
717
|
] })
|
|
616
718
|
] }),
|
|
617
|
-
/* @__PURE__ */ (0,
|
|
618
|
-
|
|
719
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
720
|
+
import_link6.default,
|
|
619
721
|
{
|
|
620
722
|
href: `/articles/${article.slug}`,
|
|
621
723
|
className: "inline-flex items-center justify-center h-8 w-8 p-0 rounded-md hover:bg-accent transition-colors",
|
|
622
724
|
"aria-label": `Read ${article.title}`,
|
|
623
|
-
children: /* @__PURE__ */ (0,
|
|
725
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.ArrowRight, { className: "h-4 w-4" })
|
|
624
726
|
}
|
|
625
727
|
)
|
|
626
728
|
] })
|
|
@@ -628,18 +730,114 @@ function ArticleCard({ article }) {
|
|
|
628
730
|
] });
|
|
629
731
|
}
|
|
630
732
|
|
|
733
|
+
// src/PaginationNav.tsx
|
|
734
|
+
var import_link7 = __toESM(require("next/link"), 1);
|
|
735
|
+
var import_lucide_react5 = require("lucide-react");
|
|
736
|
+
|
|
737
|
+
// src/pagination.ts
|
|
738
|
+
function buildPageUrl(basePath, page) {
|
|
739
|
+
const base = basePath.replace(/\/$/, "");
|
|
740
|
+
return page > 1 ? `${base}/page/${page}` : base;
|
|
741
|
+
}
|
|
742
|
+
function buildPaginationLinks(basePath, page, totalPages) {
|
|
743
|
+
return {
|
|
744
|
+
canonicalUrl: buildPageUrl(basePath, page),
|
|
745
|
+
prevUrl: page > 1 ? buildPageUrl(basePath, page - 1) : null,
|
|
746
|
+
nextUrl: page < totalPages ? buildPageUrl(basePath, page + 1) : null
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// src/PaginationNav.tsx
|
|
751
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
752
|
+
function PaginationNav({ basePath, page, totalPages }) {
|
|
753
|
+
if (totalPages <= 1) return null;
|
|
754
|
+
const { prevUrl, nextUrl } = buildPaginationLinks(basePath, page, totalPages);
|
|
755
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
756
|
+
"nav",
|
|
757
|
+
{
|
|
758
|
+
className: "mt-10 flex items-center justify-center gap-4",
|
|
759
|
+
"aria-label": "Article listing pagination",
|
|
760
|
+
children: [
|
|
761
|
+
prevUrl && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("link", { rel: "prev", href: prevUrl }),
|
|
762
|
+
nextUrl && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("link", { rel: "next", href: nextUrl }),
|
|
763
|
+
prevUrl ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
764
|
+
import_link7.default,
|
|
765
|
+
{
|
|
766
|
+
href: prevUrl,
|
|
767
|
+
className: "inline-flex items-center gap-1 px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors",
|
|
768
|
+
children: [
|
|
769
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.ChevronLeft, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
770
|
+
"Previous"
|
|
771
|
+
]
|
|
772
|
+
}
|
|
773
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
774
|
+
"span",
|
|
775
|
+
{
|
|
776
|
+
"aria-hidden": "true",
|
|
777
|
+
className: "inline-flex items-center gap-1 px-4 py-2 rounded-md font-medium text-sm text-muted-foreground opacity-50",
|
|
778
|
+
children: [
|
|
779
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.ChevronLeft, { className: "h-4 w-4" }),
|
|
780
|
+
"Previous"
|
|
781
|
+
]
|
|
782
|
+
}
|
|
783
|
+
),
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-sm text-muted-foreground", children: [
|
|
785
|
+
"Page ",
|
|
786
|
+
page,
|
|
787
|
+
" of ",
|
|
788
|
+
totalPages
|
|
789
|
+
] }),
|
|
790
|
+
nextUrl ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
791
|
+
import_link7.default,
|
|
792
|
+
{
|
|
793
|
+
href: nextUrl,
|
|
794
|
+
className: "inline-flex items-center gap-1 px-4 py-2 border border-input bg-background hover:bg-accent rounded-md font-medium text-sm transition-colors",
|
|
795
|
+
children: [
|
|
796
|
+
"Next",
|
|
797
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.ChevronRight, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
798
|
+
]
|
|
799
|
+
}
|
|
800
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
801
|
+
"span",
|
|
802
|
+
{
|
|
803
|
+
"aria-hidden": "true",
|
|
804
|
+
className: "inline-flex items-center gap-1 px-4 py-2 rounded-md font-medium text-sm text-muted-foreground opacity-50",
|
|
805
|
+
children: [
|
|
806
|
+
"Next",
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.ChevronRight, { className: "h-4 w-4" })
|
|
808
|
+
]
|
|
809
|
+
}
|
|
810
|
+
)
|
|
811
|
+
]
|
|
812
|
+
}
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
|
|
631
816
|
// src/LatestArticles.tsx
|
|
632
|
-
var
|
|
633
|
-
function LatestArticles({ articles, pageSize }) {
|
|
817
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
818
|
+
function LatestArticles({ articles, pageSize, pagination }) {
|
|
634
819
|
const [visibleCount, setVisibleCount] = (0, import_react2.useState)(pageSize);
|
|
635
820
|
(0, import_react2.useEffect)(() => {
|
|
636
821
|
setVisibleCount(pageSize);
|
|
637
822
|
}, [articles, pageSize]);
|
|
823
|
+
if (pagination) {
|
|
824
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-8", children: articles.map((article) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ArticleCard, { article }, article.slug)) }),
|
|
826
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
827
|
+
PaginationNav,
|
|
828
|
+
{
|
|
829
|
+
basePath: pagination.basePath,
|
|
830
|
+
page: pagination.page,
|
|
831
|
+
totalPages: pagination.totalPages
|
|
832
|
+
}
|
|
833
|
+
)
|
|
834
|
+
] });
|
|
835
|
+
}
|
|
638
836
|
const visible = articles.slice(0, visibleCount);
|
|
639
837
|
const hasMore = visibleCount < articles.length;
|
|
640
|
-
return /* @__PURE__ */ (0,
|
|
641
|
-
/* @__PURE__ */ (0,
|
|
642
|
-
hasMore && /* @__PURE__ */ (0,
|
|
838
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
839
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-8", children: visible.map((article) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ArticleCard, { article }, article.slug)) }),
|
|
840
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
643
841
|
"button",
|
|
644
842
|
{
|
|
645
843
|
type: "button",
|
|
@@ -652,35 +850,36 @@ function LatestArticles({ articles, pageSize }) {
|
|
|
652
850
|
}
|
|
653
851
|
|
|
654
852
|
// src/LatestArticlesSection.tsx
|
|
655
|
-
var
|
|
853
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
656
854
|
function DescriptionText({
|
|
657
855
|
searchQuery,
|
|
658
856
|
count
|
|
659
857
|
}) {
|
|
660
858
|
const pluralSuffix = count === 1 ? "" : "s";
|
|
661
859
|
const text = searchQuery ? `Found ${count} article${pluralSuffix} matching your search.` : "Stay informed with our latest insights on voter engagement, campaign strategies, and civic technology.";
|
|
662
|
-
return /* @__PURE__ */ (0,
|
|
860
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-lg text-muted-foreground", children: text });
|
|
663
861
|
}
|
|
664
862
|
function LatestArticlesSection({
|
|
665
863
|
articles,
|
|
666
864
|
searchQuery,
|
|
667
865
|
onClearSearch,
|
|
668
|
-
pageSize = 6
|
|
866
|
+
pageSize = 6,
|
|
867
|
+
pagination
|
|
669
868
|
}) {
|
|
670
|
-
return /* @__PURE__ */ (0,
|
|
671
|
-
/* @__PURE__ */ (0,
|
|
672
|
-
/* @__PURE__ */ (0,
|
|
673
|
-
/* @__PURE__ */ (0,
|
|
869
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("section", { id: "latest", className: "py-16 bg-muted/50", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-center mb-12", children: [
|
|
871
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "text-3xl font-bold text-foreground mb-4", children: searchQuery ? "Search Results" : "Latest Articles" }),
|
|
872
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DescriptionText, { searchQuery, count: articles.length })
|
|
674
873
|
] }),
|
|
675
|
-
articles.length === 0 && searchQuery ? /* @__PURE__ */ (0,
|
|
676
|
-
/* @__PURE__ */ (0,
|
|
677
|
-
/* @__PURE__ */ (0,
|
|
678
|
-
/* @__PURE__ */ (0,
|
|
874
|
+
articles.length === 0 && searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-center py-12", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "max-w-md mx-auto", children: [
|
|
875
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react6.Search, { className: "h-12 w-12 text-muted-foreground mx-auto mb-4" }),
|
|
876
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-lg font-semibold text-foreground mb-2", children: "No articles found" }),
|
|
877
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-muted-foreground mb-4", children: [
|
|
679
878
|
`We couldn't find any articles matching "`,
|
|
680
879
|
searchQuery,
|
|
681
880
|
'". Try adjusting your search terms.'
|
|
682
881
|
] }),
|
|
683
|
-
/* @__PURE__ */ (0,
|
|
882
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
684
883
|
"button",
|
|
685
884
|
{
|
|
686
885
|
type: "button",
|
|
@@ -689,7 +888,15 @@ function LatestArticlesSection({
|
|
|
689
888
|
children: "Clear search"
|
|
690
889
|
}
|
|
691
890
|
)
|
|
692
|
-
] }) }) : /* @__PURE__ */ (0,
|
|
891
|
+
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
892
|
+
LatestArticles,
|
|
893
|
+
{
|
|
894
|
+
articles,
|
|
895
|
+
pageSize,
|
|
896
|
+
pagination: searchQuery ? void 0 : pagination
|
|
897
|
+
},
|
|
898
|
+
searchQuery
|
|
899
|
+
)
|
|
693
900
|
] }) });
|
|
694
901
|
}
|
|
695
902
|
|
|
@@ -782,11 +989,12 @@ function useArticles(initialArticles, initialCategories) {
|
|
|
782
989
|
}
|
|
783
990
|
|
|
784
991
|
// src/ArticlesPage.tsx
|
|
785
|
-
var
|
|
992
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
786
993
|
function buildThemeVars(theme) {
|
|
787
994
|
if (!theme) return {};
|
|
788
995
|
const vars = {};
|
|
789
996
|
if (theme.fontFamily) vars["--articles-font-family"] = theme.fontFamily;
|
|
997
|
+
if (theme.headerFontFamily) vars["--articles-header-font-family"] = theme.headerFontFamily;
|
|
790
998
|
if (theme.headerColor) vars["--articles-header-color"] = theme.headerColor;
|
|
791
999
|
if (theme.textColor) vars["--articles-text-color"] = theme.textColor;
|
|
792
1000
|
if (theme.backgroundColor) vars["--articles-bg-color"] = theme.backgroundColor;
|
|
@@ -811,11 +1019,12 @@ function renderSection(section, ctx, config) {
|
|
|
811
1019
|
error,
|
|
812
1020
|
handleSearch,
|
|
813
1021
|
pageSize,
|
|
814
|
-
categoriesPageSize
|
|
1022
|
+
categoriesPageSize,
|
|
1023
|
+
pagination
|
|
815
1024
|
} = ctx;
|
|
816
1025
|
switch (section) {
|
|
817
1026
|
case "hero":
|
|
818
|
-
return /* @__PURE__ */ (0,
|
|
1027
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
819
1028
|
ArticlesHero,
|
|
820
1029
|
{
|
|
821
1030
|
title: (_a = config.hero) == null ? void 0 : _a.title,
|
|
@@ -824,7 +1033,7 @@ function renderSection(section, ctx, config) {
|
|
|
824
1033
|
"hero"
|
|
825
1034
|
);
|
|
826
1035
|
case "search":
|
|
827
|
-
return /* @__PURE__ */ (0,
|
|
1036
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
828
1037
|
ArticleSearchBar,
|
|
829
1038
|
{
|
|
830
1039
|
value: searchQuery,
|
|
@@ -835,7 +1044,7 @@ function renderSection(section, ctx, config) {
|
|
|
835
1044
|
"search"
|
|
836
1045
|
);
|
|
837
1046
|
case "featured":
|
|
838
|
-
return articles.length > 0 && !searchQuery ? /* @__PURE__ */ (0,
|
|
1047
|
+
return articles.length > 0 && !searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
839
1048
|
FeaturedArticle,
|
|
840
1049
|
{
|
|
841
1050
|
article: articles[0],
|
|
@@ -845,16 +1054,16 @@ function renderSection(section, ctx, config) {
|
|
|
845
1054
|
) : null;
|
|
846
1055
|
case "latest":
|
|
847
1056
|
if (loading && articles.length === 0) {
|
|
848
|
-
return /* @__PURE__ */ (0,
|
|
849
|
-
/* @__PURE__ */ (0,
|
|
850
|
-
/* @__PURE__ */ (0,
|
|
1057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("section", { id: "latest", className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-center py-24", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "text-center", children: [
|
|
1058
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4" }),
|
|
1059
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-muted-foreground", children: "Loading articles..." })
|
|
851
1060
|
] }) }) }, "latest");
|
|
852
1061
|
}
|
|
853
1062
|
if (error) {
|
|
854
|
-
return /* @__PURE__ */ (0,
|
|
855
|
-
/* @__PURE__ */ (0,
|
|
856
|
-
/* @__PURE__ */ (0,
|
|
857
|
-
/* @__PURE__ */ (0,
|
|
1063
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("section", { id: "latest", className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "bg-red-50 border border-red-200 rounded-lg p-6 max-w-md text-center", children: [
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold text-red-800 mb-2", children: "Error Loading Articles" }),
|
|
1065
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-red-600 mb-4", children: error }),
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
858
1067
|
"button",
|
|
859
1068
|
{
|
|
860
1069
|
type: "button",
|
|
@@ -865,18 +1074,19 @@ function renderSection(section, ctx, config) {
|
|
|
865
1074
|
)
|
|
866
1075
|
] }) }) }, "latest");
|
|
867
1076
|
}
|
|
868
|
-
return /* @__PURE__ */ (0,
|
|
1077
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
869
1078
|
LatestArticlesSection,
|
|
870
1079
|
{
|
|
871
1080
|
articles: displayedArticles,
|
|
872
1081
|
searchQuery,
|
|
873
1082
|
onClearSearch: () => handleSearch(""),
|
|
874
|
-
pageSize
|
|
1083
|
+
pageSize,
|
|
1084
|
+
pagination
|
|
875
1085
|
},
|
|
876
1086
|
"latest"
|
|
877
1087
|
);
|
|
878
1088
|
case "categories":
|
|
879
|
-
return searchQuery ? null : /* @__PURE__ */ (0,
|
|
1089
|
+
return searchQuery ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
880
1090
|
ArticleCategoryGrid,
|
|
881
1091
|
{
|
|
882
1092
|
categories,
|
|
@@ -892,7 +1102,10 @@ function renderSection(section, ctx, config) {
|
|
|
892
1102
|
function ArticlesPage({
|
|
893
1103
|
config,
|
|
894
1104
|
initialArticles,
|
|
895
|
-
initialCategories
|
|
1105
|
+
initialCategories,
|
|
1106
|
+
page,
|
|
1107
|
+
totalPages,
|
|
1108
|
+
totalCount
|
|
896
1109
|
}) {
|
|
897
1110
|
var _a, _b, _c;
|
|
898
1111
|
const state = useArticles(initialArticles, initialCategories);
|
|
@@ -900,19 +1113,34 @@ function ArticlesPage({
|
|
|
900
1113
|
const pageSize = (_b = config.pageSize) != null ? _b : DEFAULT_PAGE_SIZE;
|
|
901
1114
|
const categoriesPageSize = (_c = config.categoriesPageSize) != null ? _c : DEFAULT_CATEGORIES_PAGE_SIZE;
|
|
902
1115
|
const themeVars = buildThemeVars(config.theme);
|
|
903
|
-
const
|
|
1116
|
+
const isPagesMode = config.listingPagination === "pages" && page !== void 0 && totalPages !== void 0;
|
|
1117
|
+
const currentPage = page != null ? page : 1;
|
|
1118
|
+
const effectiveLayout = isPagesMode && currentPage > 1 ? layout.filter((section) => section !== "featured") : layout;
|
|
1119
|
+
const hasFeatured = effectiveLayout.includes("featured");
|
|
904
1120
|
const articlesWithoutFeatured = hasFeatured ? state.articles.slice(1) : state.articles;
|
|
905
1121
|
const displayedArticles = state.searchQuery ? state.articles : articlesWithoutFeatured;
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1122
|
+
const pagination = isPagesMode ? { page: currentPage, totalPages, basePath: "/articles" } : void 0;
|
|
1123
|
+
const ctx = __spreadProps(__spreadValues({}, state), {
|
|
1124
|
+
displayedArticles,
|
|
1125
|
+
pageSize,
|
|
1126
|
+
categoriesPageSize,
|
|
1127
|
+
pagination
|
|
1128
|
+
});
|
|
1129
|
+
const articleCount = totalCount != null ? totalCount : state.articles.length;
|
|
1130
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: themeVars, children: [
|
|
1131
|
+
effectiveLayout.map((section) => renderSection(section, ctx, config)),
|
|
1132
|
+
state.articles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
910
1133
|
CollectionPageSchema,
|
|
911
1134
|
{
|
|
912
1135
|
title: `Articles | ${config.siteName}`,
|
|
913
|
-
description: `Browse all ${
|
|
1136
|
+
description: `Browse all ${articleCount} articles on ${config.siteName}.`,
|
|
914
1137
|
url: `${config.siteUrl}/articles`,
|
|
915
|
-
articleCount
|
|
1138
|
+
articleCount,
|
|
1139
|
+
items: displayedArticles.slice(0, pageSize).map((article, index) => ({
|
|
1140
|
+
position: index + 1,
|
|
1141
|
+
url: `${config.siteUrl}/articles/${article.slug}`,
|
|
1142
|
+
name: article.title
|
|
1143
|
+
}))
|
|
916
1144
|
}
|
|
917
1145
|
)
|
|
918
1146
|
] });
|
|
@@ -920,10 +1148,10 @@ function ArticlesPage({
|
|
|
920
1148
|
|
|
921
1149
|
// src/ArticleDetailHero.tsx
|
|
922
1150
|
var import_react4 = require("react");
|
|
923
|
-
var
|
|
924
|
-
var
|
|
925
|
-
var
|
|
926
|
-
var
|
|
1151
|
+
var import_image5 = __toESM(require("next/image"), 1);
|
|
1152
|
+
var import_link8 = __toESM(require("next/link"), 1);
|
|
1153
|
+
var import_lucide_react7 = require("lucide-react");
|
|
1154
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
927
1155
|
function toSlug(cat) {
|
|
928
1156
|
return cat.toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-]/g, "");
|
|
929
1157
|
}
|
|
@@ -932,11 +1160,13 @@ function ArticleDetailHero({
|
|
|
932
1160
|
categoryBasePath = "/articles/category",
|
|
933
1161
|
showDate = false,
|
|
934
1162
|
showAuthor = true,
|
|
935
|
-
authors = []
|
|
1163
|
+
authors = [],
|
|
1164
|
+
config
|
|
936
1165
|
}) {
|
|
1166
|
+
var _a;
|
|
937
1167
|
const showConfiguredAuthors = showAuthor && authors.length > 0;
|
|
938
1168
|
const showLegacyAuthor = showAuthor && authors.length === 0 && article.author.trim().length > 0;
|
|
939
|
-
return /* @__PURE__ */ (0,
|
|
1169
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
940
1170
|
"section",
|
|
941
1171
|
{
|
|
942
1172
|
style: {
|
|
@@ -948,8 +1178,8 @@ function ArticleDetailHero({
|
|
|
948
1178
|
overflow: "hidden"
|
|
949
1179
|
},
|
|
950
1180
|
children: [
|
|
951
|
-
/* @__PURE__ */ (0,
|
|
952
|
-
|
|
1181
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1182
|
+
import_image5.default,
|
|
953
1183
|
{
|
|
954
1184
|
src: article.featuredImage,
|
|
955
1185
|
alt: article.title,
|
|
@@ -960,7 +1190,7 @@ function ArticleDetailHero({
|
|
|
960
1190
|
priority: true
|
|
961
1191
|
}
|
|
962
1192
|
),
|
|
963
|
-
/* @__PURE__ */ (0,
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
964
1194
|
"div",
|
|
965
1195
|
{
|
|
966
1196
|
style: {
|
|
@@ -970,7 +1200,7 @@ function ArticleDetailHero({
|
|
|
970
1200
|
}
|
|
971
1201
|
}
|
|
972
1202
|
),
|
|
973
|
-
/* @__PURE__ */ (0,
|
|
1203
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
974
1204
|
"div",
|
|
975
1205
|
{
|
|
976
1206
|
style: {
|
|
@@ -984,7 +1214,7 @@ function ArticleDetailHero({
|
|
|
984
1214
|
width: "100%"
|
|
985
1215
|
},
|
|
986
1216
|
children: [
|
|
987
|
-
article.categories && article.categories.length > 0 && /* @__PURE__ */ (0,
|
|
1217
|
+
article.categories && article.categories.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
988
1218
|
"div",
|
|
989
1219
|
{
|
|
990
1220
|
style: {
|
|
@@ -995,15 +1225,15 @@ function ArticleDetailHero({
|
|
|
995
1225
|
marginBottom: "1rem"
|
|
996
1226
|
},
|
|
997
1227
|
children: article.categories.slice(0, 4).map(
|
|
998
|
-
(cat) => categoryBasePath ? /* @__PURE__ */ (0,
|
|
999
|
-
|
|
1228
|
+
(cat) => categoryBasePath ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1229
|
+
import_link8.default,
|
|
1000
1230
|
{
|
|
1001
1231
|
href: `${categoryBasePath}/${toSlug(cat)}`,
|
|
1002
1232
|
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full hover:bg-white/30 transition-colors",
|
|
1003
1233
|
children: cat
|
|
1004
1234
|
},
|
|
1005
1235
|
cat
|
|
1006
|
-
) : /* @__PURE__ */ (0,
|
|
1236
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1007
1237
|
"span",
|
|
1008
1238
|
{
|
|
1009
1239
|
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full",
|
|
@@ -1014,8 +1244,15 @@ function ArticleDetailHero({
|
|
|
1014
1244
|
)
|
|
1015
1245
|
}
|
|
1016
1246
|
),
|
|
1017
|
-
/* @__PURE__ */ (0,
|
|
1018
|
-
|
|
1247
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1248
|
+
"h1",
|
|
1249
|
+
{
|
|
1250
|
+
className: "text-4xl md:text-5xl font-bold mb-4",
|
|
1251
|
+
style: { fontFamily: (_a = config == null ? void 0 : config.theme) == null ? void 0 : _a.headerFontFamily },
|
|
1252
|
+
children: article.title
|
|
1253
|
+
}
|
|
1254
|
+
),
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1019
1256
|
"div",
|
|
1020
1257
|
{
|
|
1021
1258
|
style: {
|
|
@@ -1027,23 +1264,23 @@ function ArticleDetailHero({
|
|
|
1027
1264
|
color: "rgba(255,255,255,0.8)"
|
|
1028
1265
|
},
|
|
1029
1266
|
children: [
|
|
1030
|
-
showDate && article.date && /* @__PURE__ */ (0,
|
|
1031
|
-
/* @__PURE__ */ (0,
|
|
1032
|
-
/* @__PURE__ */ (0,
|
|
1267
|
+
showDate && article.date && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1268
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Calendar, { className: "h-4 w-4" }),
|
|
1269
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: article.date })
|
|
1033
1270
|
] }),
|
|
1034
|
-
/* @__PURE__ */ (0,
|
|
1035
|
-
/* @__PURE__ */ (0,
|
|
1036
|
-
/* @__PURE__ */ (0,
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1272
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Clock, { className: "h-4 w-4" }),
|
|
1273
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: article.readTime })
|
|
1037
1274
|
] }),
|
|
1038
|
-
showConfiguredAuthors && /* @__PURE__ */ (0,
|
|
1039
|
-
/* @__PURE__ */ (0,
|
|
1040
|
-
/* @__PURE__ */ (0,
|
|
1275
|
+
showConfiguredAuthors && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.User, { className: "h-4 w-4" }),
|
|
1277
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
1041
1278
|
"By",
|
|
1042
1279
|
" ",
|
|
1043
|
-
authors.map((author, index) => /* @__PURE__ */ (0,
|
|
1280
|
+
authors.map((author, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react4.Fragment, { children: [
|
|
1044
1281
|
index > 0 && ", ",
|
|
1045
|
-
/* @__PURE__ */ (0,
|
|
1046
|
-
|
|
1282
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1283
|
+
import_link8.default,
|
|
1047
1284
|
{
|
|
1048
1285
|
href: `/articles/authors/${author.slug}`,
|
|
1049
1286
|
className: "font-medium text-white underline-offset-4 hover:underline",
|
|
@@ -1053,15 +1290,26 @@ function ArticleDetailHero({
|
|
|
1053
1290
|
] }, author.slug))
|
|
1054
1291
|
] })
|
|
1055
1292
|
] }),
|
|
1056
|
-
showLegacyAuthor && /* @__PURE__ */ (0,
|
|
1057
|
-
/* @__PURE__ */ (0,
|
|
1058
|
-
/* @__PURE__ */ (0,
|
|
1293
|
+
showLegacyAuthor && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.User, { className: "h-4 w-4" }),
|
|
1295
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
1059
1296
|
"By ",
|
|
1060
1297
|
article.author
|
|
1061
1298
|
] })
|
|
1062
1299
|
] })
|
|
1063
1300
|
]
|
|
1064
1301
|
}
|
|
1302
|
+
),
|
|
1303
|
+
showConfiguredAuthors && authors.length === 1 && authors[0].promise && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1304
|
+
"p",
|
|
1305
|
+
{
|
|
1306
|
+
style: {
|
|
1307
|
+
marginTop: "0.75rem",
|
|
1308
|
+
fontSize: "0.9375rem",
|
|
1309
|
+
color: "rgba(255,255,255,0.85)"
|
|
1310
|
+
},
|
|
1311
|
+
children: authors[0].promise
|
|
1312
|
+
}
|
|
1065
1313
|
)
|
|
1066
1314
|
]
|
|
1067
1315
|
}
|
|
@@ -1072,9 +1320,9 @@ function ArticleDetailHero({
|
|
|
1072
1320
|
}
|
|
1073
1321
|
|
|
1074
1322
|
// src/CategoryArticlesPage.tsx
|
|
1075
|
-
var
|
|
1076
|
-
var
|
|
1077
|
-
var
|
|
1323
|
+
var import_image6 = __toESM(require("next/image"), 1);
|
|
1324
|
+
var import_link9 = __toESM(require("next/link"), 1);
|
|
1325
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1078
1326
|
function getCategoryDescription(config, slug, categoryName) {
|
|
1079
1327
|
var _a;
|
|
1080
1328
|
const raw = (_a = config.categoryDescriptions) == null ? void 0 : _a[slug];
|
|
@@ -1116,7 +1364,14 @@ function buildCategoryBreadcrumbEntry(entry, context) {
|
|
|
1116
1364
|
}
|
|
1117
1365
|
return [{ name: context.categoryName }];
|
|
1118
1366
|
}
|
|
1119
|
-
function CategoryArticlesPage({
|
|
1367
|
+
function CategoryArticlesPage({
|
|
1368
|
+
category,
|
|
1369
|
+
articles,
|
|
1370
|
+
config,
|
|
1371
|
+
page,
|
|
1372
|
+
totalPages,
|
|
1373
|
+
totalCount
|
|
1374
|
+
}) {
|
|
1120
1375
|
var _a;
|
|
1121
1376
|
if (articles.length === 0) return null;
|
|
1122
1377
|
const categoryName = articles[0].category;
|
|
@@ -1125,8 +1380,25 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1125
1380
|
const pageSize = (_a = config.pageSize) != null ? _a : DEFAULT_PAGE_SIZE;
|
|
1126
1381
|
const breadcrumbConfig = getBreadcrumbsConfig(config);
|
|
1127
1382
|
const breadcrumbItems = buildCategoryBreadcrumbItems(config, categoryName);
|
|
1128
|
-
|
|
1129
|
-
|
|
1383
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
1384
|
+
const articleCount = totalCount != null ? totalCount : articles.length;
|
|
1385
|
+
const pagination = config.listingPagination === "pages" && page !== void 0 && totalPages !== void 0 ? { page, totalPages, basePath: `/articles/category/${category}` } : void 0;
|
|
1386
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
1387
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1388
|
+
CollectionPageSchema,
|
|
1389
|
+
{
|
|
1390
|
+
title: `${categoryName} | ${config.siteName}`,
|
|
1391
|
+
description: description.short,
|
|
1392
|
+
url: `${siteUrl}/articles/category/${category}`,
|
|
1393
|
+
articleCount,
|
|
1394
|
+
items: articles.slice(0, pageSize).map((article, index) => ({
|
|
1395
|
+
position: index + 1,
|
|
1396
|
+
url: `${siteUrl}/articles/${article.slug}`,
|
|
1397
|
+
name: article.title
|
|
1398
|
+
}))
|
|
1399
|
+
}
|
|
1400
|
+
),
|
|
1401
|
+
breadcrumbsAreEnabled(config) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1130
1402
|
Breadcrumb,
|
|
1131
1403
|
{
|
|
1132
1404
|
items: breadcrumbItems,
|
|
@@ -1134,7 +1406,7 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1134
1406
|
showSchema: breadcrumbConfig.showSchema !== false
|
|
1135
1407
|
}
|
|
1136
1408
|
),
|
|
1137
|
-
/* @__PURE__ */ (0,
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
1138
1410
|
"section",
|
|
1139
1411
|
{
|
|
1140
1412
|
style: {
|
|
@@ -1146,8 +1418,8 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1146
1418
|
overflow: "hidden"
|
|
1147
1419
|
},
|
|
1148
1420
|
children: [
|
|
1149
|
-
/* @__PURE__ */ (0,
|
|
1150
|
-
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1422
|
+
import_image6.default,
|
|
1151
1423
|
{
|
|
1152
1424
|
src: heroImage,
|
|
1153
1425
|
alt: categoryName,
|
|
@@ -1157,8 +1429,8 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1157
1429
|
priority: true
|
|
1158
1430
|
}
|
|
1159
1431
|
),
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1161
|
-
/* @__PURE__ */ (0,
|
|
1432
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { position: "absolute", inset: 0, backgroundColor: "rgba(0,0,0,0.6)" } }),
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
1162
1434
|
"div",
|
|
1163
1435
|
{
|
|
1164
1436
|
style: {
|
|
@@ -1169,10 +1441,10 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1169
1441
|
padding: "0 1rem"
|
|
1170
1442
|
},
|
|
1171
1443
|
children: [
|
|
1172
|
-
/* @__PURE__ */ (0,
|
|
1173
|
-
/* @__PURE__ */ (0,
|
|
1174
|
-
description.long && /* @__PURE__ */ (0,
|
|
1175
|
-
/* @__PURE__ */ (0,
|
|
1444
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-semibold uppercase tracking-widest mb-3 opacity-80", children: "Category" }),
|
|
1445
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-3", children: categoryName }),
|
|
1446
|
+
description.long && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-lg opacity-90 max-w-2xl mx-auto mt-2", children: description.long }),
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "text-lg opacity-70 mt-2", children: [
|
|
1176
1448
|
articles.length,
|
|
1177
1449
|
" article",
|
|
1178
1450
|
articles.length === 1 ? "" : "s"
|
|
@@ -1183,140 +1455,75 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1183
1455
|
]
|
|
1184
1456
|
}
|
|
1185
1457
|
),
|
|
1186
|
-
/* @__PURE__ */ (0,
|
|
1187
|
-
/* @__PURE__ */ (0,
|
|
1188
|
-
/* @__PURE__ */ (0,
|
|
1189
|
-
/* @__PURE__ */ (0,
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("section", { className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
1459
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
|
|
1460
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_link9.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
|
|
1461
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: description.short })
|
|
1190
1462
|
] }),
|
|
1191
|
-
/* @__PURE__ */ (0,
|
|
1463
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LatestArticles, { articles, pageSize, pagination })
|
|
1192
1464
|
] }) })
|
|
1193
1465
|
] });
|
|
1194
1466
|
}
|
|
1195
1467
|
|
|
1196
|
-
// src/
|
|
1197
|
-
var
|
|
1198
|
-
var
|
|
1199
|
-
function
|
|
1200
|
-
var _a, _b;
|
|
1201
|
-
return __spreadProps(__spreadValues(__spreadValues({
|
|
1202
|
-
"@context": "https://schema.org",
|
|
1203
|
-
"@type": "Person",
|
|
1204
|
-
name: author.name,
|
|
1205
|
-
description: author.bio,
|
|
1206
|
-
url: (_a = author.url) != null ? _a : getAuthorUrl(author)
|
|
1207
|
-
}, getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }), {
|
|
1208
|
-
knowsAbout: config.siteName,
|
|
1209
|
-
mainEntityOfPage: (_b = author.url) != null ? _b : `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`,
|
|
1210
|
-
interactionStatistic: {
|
|
1211
|
-
"@type": "InteractionCounter",
|
|
1212
|
-
interactionType: "https://schema.org/WriteAction",
|
|
1213
|
-
userInteractionCount: articleCount
|
|
1214
|
-
}
|
|
1215
|
-
});
|
|
1216
|
-
}
|
|
1217
|
-
function AuthorArticlesPage({ author, articles, config }) {
|
|
1468
|
+
// src/SeriesArticlesPage.tsx
|
|
1469
|
+
var import_link10 = __toESM(require("next/link"), 1);
|
|
1470
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1471
|
+
function SeriesArticlesPage({ seriesSlug, articles, config }) {
|
|
1218
1472
|
var _a;
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1473
|
+
if (articles.length === 0) return null;
|
|
1474
|
+
const seriesName = (_a = articles[0].series) != null ? _a : seriesSlug;
|
|
1475
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
1476
|
+
const seriesUrl = `${siteUrl}/articles/series/${seriesSlug}`;
|
|
1477
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
1478
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1479
|
+
CollectionPageSchema,
|
|
1223
1480
|
{
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1481
|
+
title: `${seriesName} | ${config.siteName}`,
|
|
1482
|
+
description: `Follow the ${seriesName} series on ${config.siteName}.`,
|
|
1483
|
+
url: seriesUrl,
|
|
1484
|
+
articleCount: articles.length,
|
|
1485
|
+
items: articles.map((article, index) => ({
|
|
1486
|
+
position: index + 1,
|
|
1487
|
+
url: `${siteUrl}/articles/${article.slug}`,
|
|
1488
|
+
name: article.title
|
|
1489
|
+
}))
|
|
1228
1490
|
}
|
|
1229
1491
|
),
|
|
1230
|
-
/* @__PURE__ */ (0,
|
|
1231
|
-
/* @__PURE__ */ (0,
|
|
1232
|
-
/* @__PURE__ */ (0,
|
|
1233
|
-
/* @__PURE__ */ (0,
|
|
1234
|
-
|
|
1235
|
-
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("section", { className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
|
|
1494
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_link10.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
|
|
1495
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1496
|
+
articles.length,
|
|
1497
|
+
" article",
|
|
1498
|
+
articles.length === 1 ? "" : "s",
|
|
1499
|
+
" in this series"
|
|
1236
1500
|
] })
|
|
1237
1501
|
] }),
|
|
1238
|
-
/* @__PURE__ */ (0,
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h1", { className: "text-3xl font-bold text-foreground mb-8", children: seriesName }),
|
|
1503
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1504
|
+
LatestArticles,
|
|
1505
|
+
{
|
|
1506
|
+
articles,
|
|
1507
|
+
pageSize: Math.max(articles.length, DEFAULT_PAGE_SIZE)
|
|
1508
|
+
}
|
|
1509
|
+
)
|
|
1239
1510
|
] }) })
|
|
1240
1511
|
] });
|
|
1241
1512
|
}
|
|
1242
1513
|
|
|
1243
|
-
// src/
|
|
1244
|
-
var
|
|
1245
|
-
var import_link9 = __toESM(require("next/link"), 1);
|
|
1246
|
-
var import_lucide_react6 = require("lucide-react");
|
|
1247
|
-
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1248
|
-
function getInitials(name) {
|
|
1249
|
-
return name.split(" ").filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
|
|
1250
|
-
}
|
|
1251
|
-
function AuthorCard({
|
|
1252
|
-
author,
|
|
1253
|
-
linkToPage = true,
|
|
1254
|
-
showBio = false,
|
|
1255
|
-
showSocial = false
|
|
1256
|
-
}) {
|
|
1257
|
-
const avatar = getAuthorAvatar(author);
|
|
1258
|
-
const name = linkToPage ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_link9.default, { href: getAuthorUrl(author), className: "font-medium text-foreground hover:text-primary", children: author.name }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-medium text-foreground", children: author.name });
|
|
1259
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-start gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground", children: [
|
|
1260
|
-
avatar ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1261
|
-
import_image6.default,
|
|
1262
|
-
{
|
|
1263
|
-
src: avatar,
|
|
1264
|
-
alt: author.name,
|
|
1265
|
-
width: 48,
|
|
1266
|
-
height: 48,
|
|
1267
|
-
className: "h-12 w-12 rounded-full object-cover"
|
|
1268
|
-
}
|
|
1269
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-semibold text-muted-foreground", children: getInitials(author.name) }),
|
|
1270
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1271
|
-
name,
|
|
1272
|
-
showBio && author.bio && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mt-1 line-clamp-2 text-sm text-muted-foreground", children: author.bio }),
|
|
1273
|
-
showSocial && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AuthorSocialLinks, { author })
|
|
1274
|
-
] })
|
|
1275
|
-
] });
|
|
1276
|
-
}
|
|
1277
|
-
var SOCIAL_ICONS = {
|
|
1278
|
-
Website: import_lucide_react6.Globe,
|
|
1279
|
-
Facebook: import_lucide_react6.Facebook,
|
|
1280
|
-
Twitter: import_lucide_react6.Twitter,
|
|
1281
|
-
X: import_lucide_react6.Twitter,
|
|
1282
|
-
LinkedIn: import_lucide_react6.Linkedin,
|
|
1283
|
-
Instagram: import_lucide_react6.Instagram,
|
|
1284
|
-
YouTube: import_lucide_react6.Youtube,
|
|
1285
|
-
GitHub: import_lucide_react6.Github,
|
|
1286
|
-
Newsletter: import_lucide_react6.Mail
|
|
1287
|
-
};
|
|
1288
|
-
function getSocialIcon(label) {
|
|
1289
|
-
var _a;
|
|
1290
|
-
return (_a = SOCIAL_ICONS[label]) != null ? _a : import_lucide_react6.MessageCircle;
|
|
1291
|
-
}
|
|
1292
|
-
function AuthorSocialLinks({ author }) {
|
|
1293
|
-
const links = getAuthorSocialLinks(author);
|
|
1294
|
-
if (links.length === 0) return null;
|
|
1295
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "mt-3 flex items-center gap-2", children: links.map(({ href, label }) => {
|
|
1296
|
-
const Icon = getSocialIcon(label);
|
|
1297
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1298
|
-
import_link9.default,
|
|
1299
|
-
{
|
|
1300
|
-
href,
|
|
1301
|
-
className: "inline-flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
1302
|
-
"aria-label": `${author.name} on ${label}`,
|
|
1303
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { className: "h-4 w-4" })
|
|
1304
|
-
},
|
|
1305
|
-
`${label}-${href}`
|
|
1306
|
-
);
|
|
1307
|
-
}) });
|
|
1308
|
-
}
|
|
1514
|
+
// src/AuthorArticlesPage.tsx
|
|
1515
|
+
var import_link11 = __toESM(require("next/link"), 1);
|
|
1309
1516
|
|
|
1310
1517
|
// src/AuthorDetailHero.tsx
|
|
1311
1518
|
var import_image7 = __toESM(require("next/image"), 1);
|
|
1312
|
-
var
|
|
1519
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1313
1520
|
function getInitials2(name) {
|
|
1314
1521
|
return name.split(" ").filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
|
|
1315
1522
|
}
|
|
1316
1523
|
function AuthorDetailHero({ author, articleCount }) {
|
|
1317
1524
|
const avatar = getAuthorAvatar(author);
|
|
1318
|
-
return /* @__PURE__ */ (0,
|
|
1319
|
-
avatar ? /* @__PURE__ */ (0,
|
|
1525
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("section", { className: "bg-muted/40 py-16", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "mx-auto flex max-w-4xl flex-col items-center gap-6 px-4 text-center sm:px-6 lg:px-8", children: [
|
|
1526
|
+
avatar ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1320
1527
|
import_image7.default,
|
|
1321
1528
|
{
|
|
1322
1529
|
src: avatar,
|
|
@@ -1326,27 +1533,303 @@ function AuthorDetailHero({ author, articleCount }) {
|
|
|
1326
1533
|
className: "h-28 w-28 rounded-full object-cover",
|
|
1327
1534
|
priority: true
|
|
1328
1535
|
}
|
|
1329
|
-
) : /* @__PURE__ */ (0,
|
|
1330
|
-
/* @__PURE__ */ (0,
|
|
1331
|
-
/* @__PURE__ */ (0,
|
|
1332
|
-
/* @__PURE__ */ (0,
|
|
1333
|
-
/* @__PURE__ */ (0,
|
|
1334
|
-
typeof articleCount === "number" && /* @__PURE__ */ (0,
|
|
1536
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex h-28 w-28 items-center justify-center rounded-full bg-card text-3xl font-semibold text-muted-foreground shadow-sm", children: getInitials2(author.name) }),
|
|
1537
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
|
|
1538
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-widest text-muted-foreground", children: "Author" }),
|
|
1539
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h1", { className: "text-4xl font-bold text-foreground md:text-5xl", children: author.name }),
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mx-auto mt-4 max-w-2xl text-lg text-muted-foreground", children: author.bio }),
|
|
1541
|
+
typeof articleCount === "number" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "mt-3 text-sm text-muted-foreground", children: [
|
|
1335
1542
|
articleCount,
|
|
1336
1543
|
" article",
|
|
1337
1544
|
articleCount === 1 ? "" : "s"
|
|
1338
1545
|
] })
|
|
1339
1546
|
] }),
|
|
1340
|
-
/* @__PURE__ */ (0,
|
|
1547
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AuthorSocialLinks, { author })
|
|
1341
1548
|
] }) });
|
|
1342
1549
|
}
|
|
1343
1550
|
|
|
1344
|
-
// src/
|
|
1551
|
+
// src/eventTracking.tsx
|
|
1345
1552
|
var import_react5 = require("react");
|
|
1346
|
-
var
|
|
1347
|
-
var
|
|
1348
|
-
|
|
1349
|
-
|
|
1553
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1554
|
+
var AVERAGE_WORDS_PER_MINUTE = 200;
|
|
1555
|
+
var DEFAULT_MEANINGFUL_READ_MS = 15e3;
|
|
1556
|
+
function ArticleViewTracker({ article, config }) {
|
|
1557
|
+
const firedMeaningfulRead = (0, import_react5.useRef)(false);
|
|
1558
|
+
(0, import_react5.useEffect)(() => {
|
|
1559
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
1560
|
+
name: "article_viewed",
|
|
1561
|
+
articleSlug: article.slug,
|
|
1562
|
+
category: article.category,
|
|
1563
|
+
seriesSlug: article.seriesSlug
|
|
1564
|
+
});
|
|
1565
|
+
const estimatedMs = article.wordCount ? article.wordCount / AVERAGE_WORDS_PER_MINUTE * 6e4 * 0.5 : DEFAULT_MEANINGFUL_READ_MS;
|
|
1566
|
+
const timer = setTimeout(() => {
|
|
1567
|
+
if (firedMeaningfulRead.current) return;
|
|
1568
|
+
firedMeaningfulRead.current = true;
|
|
1569
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, { name: "meaningful_read", articleSlug: article.slug });
|
|
1570
|
+
}, estimatedMs);
|
|
1571
|
+
return () => clearTimeout(timer);
|
|
1572
|
+
}, [article.slug]);
|
|
1573
|
+
return null;
|
|
1574
|
+
}
|
|
1575
|
+
function CtaViewTracker({ ctaId, articleSlug, config, children }) {
|
|
1576
|
+
const ref = (0, import_react5.useRef)(null);
|
|
1577
|
+
const fired = (0, import_react5.useRef)(false);
|
|
1578
|
+
(0, import_react5.useEffect)(() => {
|
|
1579
|
+
const node = ref.current;
|
|
1580
|
+
if (!node) return;
|
|
1581
|
+
function fire() {
|
|
1582
|
+
if (fired.current) return;
|
|
1583
|
+
fired.current = true;
|
|
1584
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, { name: "cta_viewed", ctaId, articleSlug });
|
|
1585
|
+
}
|
|
1586
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
1587
|
+
fire();
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
const observer = new IntersectionObserver(
|
|
1591
|
+
(entries) => {
|
|
1592
|
+
if (entries.some((entry) => entry.isIntersecting)) fire();
|
|
1593
|
+
},
|
|
1594
|
+
{ threshold: 0.5 }
|
|
1595
|
+
);
|
|
1596
|
+
observer.observe(node);
|
|
1597
|
+
return () => observer.disconnect();
|
|
1598
|
+
}, [ctaId, articleSlug]);
|
|
1599
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref, children });
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// src/AuthorArticlesPage.tsx
|
|
1603
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1604
|
+
function getPersonSchema(author, config, articleCount) {
|
|
1605
|
+
var _a, _b;
|
|
1606
|
+
return __spreadProps(__spreadValues(__spreadValues({
|
|
1607
|
+
"@context": "https://schema.org",
|
|
1608
|
+
"@type": "Person",
|
|
1609
|
+
name: author.name,
|
|
1610
|
+
description: author.bio,
|
|
1611
|
+
url: (_a = author.url) != null ? _a : getAuthorUrl(author)
|
|
1612
|
+
}, getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }), {
|
|
1613
|
+
knowsAbout: config.siteName,
|
|
1614
|
+
mainEntityOfPage: (_b = author.url) != null ? _b : `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`,
|
|
1615
|
+
interactionStatistic: {
|
|
1616
|
+
"@type": "InteractionCounter",
|
|
1617
|
+
interactionType: "https://schema.org/WriteAction",
|
|
1618
|
+
userInteractionCount: articleCount
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
function renderHeroSection(author, articleCount) {
|
|
1623
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AuthorDetailHero, { author, articleCount }, "hero");
|
|
1624
|
+
}
|
|
1625
|
+
function renderPromiseSection(author) {
|
|
1626
|
+
return author.promise ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("section", { "aria-label": "Author promise", className: "bg-background py-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mx-auto max-w-3xl px-4 text-center sm:px-6 lg:px-8", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-xl font-medium text-foreground md:text-2xl", children: author.promise }) }) }, "promise") : null;
|
|
1627
|
+
}
|
|
1628
|
+
function renderServesWhoSection(author) {
|
|
1629
|
+
return author.servesWho && author.servesWho.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("section", { "aria-label": "Who this author serves", className: "bg-muted/40 py-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mx-auto max-w-3xl px-4 sm:px-6 lg:px-8", children: [
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "mb-4 text-center text-2xl font-bold text-foreground", children: "Who I help" }),
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "grid gap-3 sm:grid-cols-2", children: author.servesWho.map((who) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1632
|
+
"li",
|
|
1633
|
+
{
|
|
1634
|
+
className: "rounded-lg border border-border bg-card px-4 py-3 text-sm text-card-foreground",
|
|
1635
|
+
children: who
|
|
1636
|
+
},
|
|
1637
|
+
who
|
|
1638
|
+
)) })
|
|
1639
|
+
] }) }, "servesWho") : null;
|
|
1640
|
+
}
|
|
1641
|
+
function renderOriginStorySection(author) {
|
|
1642
|
+
return author.originStory && author.originStory.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("section", { "aria-label": "Author origin story", className: "bg-background py-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mx-auto max-w-3xl px-4 sm:px-6 lg:px-8", children: [
|
|
1643
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "mb-6 text-2xl font-bold text-foreground", children: "My story" }),
|
|
1644
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "space-y-6", children: author.originStory.map((block, blockIndex) => {
|
|
1645
|
+
var _a;
|
|
1646
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
1647
|
+
block.heading && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { className: "mb-2 text-lg font-semibold text-foreground", children: block.heading }),
|
|
1648
|
+
block.paragraphs.map((paragraph, paragraphIndex) => {
|
|
1649
|
+
var _a2;
|
|
1650
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1651
|
+
"p",
|
|
1652
|
+
{
|
|
1653
|
+
className: "mb-3 text-muted-foreground",
|
|
1654
|
+
children: paragraph
|
|
1655
|
+
},
|
|
1656
|
+
`${(_a2 = block.heading) != null ? _a2 : blockIndex}-${paragraph.slice(0, 40)}-${paragraphIndex}`
|
|
1657
|
+
);
|
|
1658
|
+
})
|
|
1659
|
+
] }, (_a = block.heading) != null ? _a : blockIndex);
|
|
1660
|
+
}) })
|
|
1661
|
+
] }) }, "originStory") : null;
|
|
1662
|
+
}
|
|
1663
|
+
function renderPrinciplesSection(author) {
|
|
1664
|
+
return author.principles && author.principles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("section", { "aria-label": "Author principles", className: "bg-muted/40 py-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mx-auto max-w-3xl px-4 sm:px-6 lg:px-8", children: [
|
|
1665
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "mb-4 text-center text-2xl font-bold text-foreground", children: "What I believe" }),
|
|
1666
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "space-y-3", children: author.principles.map((principle) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1667
|
+
"li",
|
|
1668
|
+
{
|
|
1669
|
+
className: "rounded-lg border border-border bg-card px-4 py-3 text-sm text-card-foreground",
|
|
1670
|
+
children: principle
|
|
1671
|
+
},
|
|
1672
|
+
principle
|
|
1673
|
+
)) })
|
|
1674
|
+
] }) }, "principles") : null;
|
|
1675
|
+
}
|
|
1676
|
+
function renderProofSection(author) {
|
|
1677
|
+
return author.proof && author.proof.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("section", { "aria-label": "Author proof", className: "bg-background py-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mx-auto max-w-4xl px-4 sm:px-6 lg:px-8", children: [
|
|
1678
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "mb-6 text-center text-2xl font-bold text-foreground", children: "Proof" }),
|
|
1679
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "grid gap-4 sm:grid-cols-2", children: author.proof.map((item) => {
|
|
1680
|
+
var _a, _b, _c;
|
|
1681
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1682
|
+
"div",
|
|
1683
|
+
{
|
|
1684
|
+
className: "rounded-lg border border-border bg-card p-4 text-card-foreground",
|
|
1685
|
+
children: [
|
|
1686
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "font-medium", children: item.claim }),
|
|
1687
|
+
(item.source || item.url) && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "mt-1 text-sm text-muted-foreground", children: item.url ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_link11.default, { href: item.url, className: "hover:text-primary hover:underline", children: (_c = item.source) != null ? _c : item.url }) : item.source })
|
|
1688
|
+
]
|
|
1689
|
+
},
|
|
1690
|
+
`${item.claim}-${(_b = (_a = item.url) != null ? _a : item.source) != null ? _b : ""}`
|
|
1691
|
+
);
|
|
1692
|
+
}) })
|
|
1693
|
+
] }) }, "proof") : null;
|
|
1694
|
+
}
|
|
1695
|
+
function renderCtaSection(author, config) {
|
|
1696
|
+
return author.primaryCta ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CtaViewTracker, { ctaId: `author-cta:${author.slug}`, config, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("section", { "aria-label": "Author call to action", className: "bg-muted/40 py-12", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mx-auto max-w-2xl px-4 text-center sm:px-6 lg:px-8", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1697
|
+
import_link11.default,
|
|
1698
|
+
{
|
|
1699
|
+
href: author.primaryCta.href,
|
|
1700
|
+
onClick: () => emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
1701
|
+
name: "cta_clicked",
|
|
1702
|
+
ctaId: `author-cta:${author.slug}`
|
|
1703
|
+
}),
|
|
1704
|
+
className: "inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground hover:bg-primary/90",
|
|
1705
|
+
children: author.primaryCta.label
|
|
1706
|
+
}
|
|
1707
|
+
) }) }) }, "cta") : null;
|
|
1708
|
+
}
|
|
1709
|
+
function renderArticlesSection(author, articles, pageSize, pagination) {
|
|
1710
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("section", { "aria-label": "Articles by this author", className: "bg-background py-16", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", children: [
|
|
1711
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mb-8 flex items-center justify-between gap-4", children: [
|
|
1712
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_link11.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "All Articles" }),
|
|
1713
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1714
|
+
"Articles by ",
|
|
1715
|
+
author.name
|
|
1716
|
+
] })
|
|
1717
|
+
] }),
|
|
1718
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LatestArticles, { articles, pageSize, pagination })
|
|
1719
|
+
] }) }, "articles");
|
|
1720
|
+
}
|
|
1721
|
+
function renderAuthorSection(section, ctx) {
|
|
1722
|
+
const { author, articleCount, articles, pageSize, pagination, customSection, config } = ctx;
|
|
1723
|
+
switch (section) {
|
|
1724
|
+
case "hero":
|
|
1725
|
+
return renderHeroSection(author, articleCount);
|
|
1726
|
+
case "promise":
|
|
1727
|
+
return renderPromiseSection(author);
|
|
1728
|
+
case "servesWho":
|
|
1729
|
+
return renderServesWhoSection(author);
|
|
1730
|
+
case "originStory":
|
|
1731
|
+
return renderOriginStorySection(author);
|
|
1732
|
+
case "principles":
|
|
1733
|
+
return renderPrinciplesSection(author);
|
|
1734
|
+
case "proof":
|
|
1735
|
+
return renderProofSection(author);
|
|
1736
|
+
case "cta":
|
|
1737
|
+
return renderCtaSection(author, config);
|
|
1738
|
+
case "articles":
|
|
1739
|
+
return renderArticlesSection(author, articles, pageSize, pagination);
|
|
1740
|
+
case "custom":
|
|
1741
|
+
return customSection != null ? customSection : null;
|
|
1742
|
+
default:
|
|
1743
|
+
return null;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
function AuthorArticlesPage({
|
|
1747
|
+
author,
|
|
1748
|
+
articles,
|
|
1749
|
+
config,
|
|
1750
|
+
page,
|
|
1751
|
+
totalPages,
|
|
1752
|
+
totalCount,
|
|
1753
|
+
sections,
|
|
1754
|
+
customSection
|
|
1755
|
+
}) {
|
|
1756
|
+
var _a, _b;
|
|
1757
|
+
const pageSize = (_a = config.pageSize) != null ? _a : DEFAULT_PAGE_SIZE;
|
|
1758
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
1759
|
+
const articleCount = totalCount != null ? totalCount : articles.length;
|
|
1760
|
+
const pagination = config.listingPagination === "pages" && page !== void 0 && totalPages !== void 0 ? { page, totalPages, basePath: `/articles/authors/${author.slug}` } : void 0;
|
|
1761
|
+
const schemas = /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
|
|
1762
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1763
|
+
"script",
|
|
1764
|
+
{
|
|
1765
|
+
type: "application/ld+json",
|
|
1766
|
+
dangerouslySetInnerHTML: {
|
|
1767
|
+
__html: JSON.stringify(getPersonSchema(author, config, articleCount))
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
),
|
|
1771
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1772
|
+
CollectionPageSchema,
|
|
1773
|
+
{
|
|
1774
|
+
title: `${author.name} | ${config.siteName}`,
|
|
1775
|
+
description: `Articles by ${author.name} on ${config.siteName}.`,
|
|
1776
|
+
url: (_b = author.url) != null ? _b : `${siteUrl}/articles/authors/${author.slug}`,
|
|
1777
|
+
articleCount,
|
|
1778
|
+
items: articles.slice(0, pageSize).map((article, index) => ({
|
|
1779
|
+
position: index + 1,
|
|
1780
|
+
url: `${siteUrl}/articles/${article.slug}`,
|
|
1781
|
+
name: article.title
|
|
1782
|
+
}))
|
|
1783
|
+
}
|
|
1784
|
+
)
|
|
1785
|
+
] });
|
|
1786
|
+
if (!sections) {
|
|
1787
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
1788
|
+
schemas,
|
|
1789
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("section", { className: "bg-background py-16", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", children: [
|
|
1790
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mb-8 flex items-center justify-between gap-4", children: [
|
|
1791
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_link11.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "All Articles" }),
|
|
1792
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1793
|
+
"Articles by ",
|
|
1794
|
+
author.name
|
|
1795
|
+
] })
|
|
1796
|
+
] }),
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LatestArticles, { articles, pageSize, pagination })
|
|
1798
|
+
] }) })
|
|
1799
|
+
] });
|
|
1800
|
+
}
|
|
1801
|
+
const sectionCtx = {
|
|
1802
|
+
author,
|
|
1803
|
+
articleCount,
|
|
1804
|
+
articles,
|
|
1805
|
+
pageSize,
|
|
1806
|
+
pagination,
|
|
1807
|
+
customSection,
|
|
1808
|
+
config
|
|
1809
|
+
};
|
|
1810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
1811
|
+
schemas,
|
|
1812
|
+
sections.map((section) => renderAuthorSection(section, sectionCtx))
|
|
1813
|
+
] });
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// src/ArticleSocialShare.tsx
|
|
1817
|
+
var import_react6 = require("react");
|
|
1818
|
+
var import_lucide_react8 = require("lucide-react");
|
|
1819
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1820
|
+
function ArticleSocialShare({
|
|
1821
|
+
title,
|
|
1822
|
+
url,
|
|
1823
|
+
excerpt,
|
|
1824
|
+
shareMessage,
|
|
1825
|
+
config,
|
|
1826
|
+
articleSlug
|
|
1827
|
+
}) {
|
|
1828
|
+
const [copied, setCopied] = (0, import_react6.useState)(false);
|
|
1829
|
+
function trackShare(channel) {
|
|
1830
|
+
if (!articleSlug) return;
|
|
1831
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, { name: "shared", articleSlug, channel });
|
|
1832
|
+
}
|
|
1350
1833
|
const encodedTitle = encodeURIComponent(title);
|
|
1351
1834
|
const encodedUrl = encodeURIComponent(url);
|
|
1352
1835
|
const encodedExcerpt = encodeURIComponent(excerpt || "");
|
|
@@ -1363,102 +1846,211 @@ function ArticleSocialShare({ title, url, excerpt, shareMessage }) {
|
|
|
1363
1846
|
try {
|
|
1364
1847
|
yield navigator.clipboard.writeText(url);
|
|
1365
1848
|
setCopied(true);
|
|
1849
|
+
trackShare("copy-link");
|
|
1366
1850
|
setTimeout(() => setCopied(false), 2e3);
|
|
1367
1851
|
} catch (e) {
|
|
1368
1852
|
}
|
|
1369
1853
|
});
|
|
1370
|
-
const openShareWindow = (shareUrl) => {
|
|
1854
|
+
const openShareWindow = (shareUrl, channel) => {
|
|
1855
|
+
trackShare(channel);
|
|
1371
1856
|
globalThis.open(shareUrl, "_blank", "width=600,height=400,scrollbars=yes,resizable=yes");
|
|
1372
1857
|
};
|
|
1373
1858
|
const btnClass = "inline-flex items-center gap-2 px-3 py-1.5 text-sm rounded-md border border-border bg-background text-foreground hover:bg-primary/10 hover:border-primary hover:text-primary transition-colors cursor-pointer";
|
|
1374
|
-
return /* @__PURE__ */ (0,
|
|
1375
|
-
/* @__PURE__ */ (0,
|
|
1376
|
-
/* @__PURE__ */ (0,
|
|
1377
|
-
/* @__PURE__ */ (0,
|
|
1859
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "border-t border-border pt-8 mt-8", children: [
|
|
1860
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 mb-4", children: [
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Share2, { className: "h-5 w-5 text-muted-foreground" }),
|
|
1862
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { className: "text-lg font-semibold text-foreground", children: "Share this article" })
|
|
1378
1863
|
] }),
|
|
1379
|
-
/* @__PURE__ */ (0,
|
|
1380
|
-
/* @__PURE__ */ (0,
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
/* @__PURE__ */ (0,
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
/* @__PURE__ */ (0,
|
|
1405
|
-
|
|
1864
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-wrap gap-2", children: [
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1866
|
+
"button",
|
|
1867
|
+
{
|
|
1868
|
+
type: "button",
|
|
1869
|
+
className: btnClass,
|
|
1870
|
+
onClick: () => openShareWindow(shareLinks.linkedin, "linkedin"),
|
|
1871
|
+
children: [
|
|
1872
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Users, { className: "h-4 w-4" }),
|
|
1873
|
+
"LinkedIn"
|
|
1874
|
+
]
|
|
1875
|
+
}
|
|
1876
|
+
),
|
|
1877
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1878
|
+
"button",
|
|
1879
|
+
{
|
|
1880
|
+
type: "button",
|
|
1881
|
+
className: btnClass,
|
|
1882
|
+
onClick: () => openShareWindow(shareLinks.facebook, "facebook"),
|
|
1883
|
+
children: [
|
|
1884
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-xs font-bold", children: "f" }),
|
|
1885
|
+
" Facebook"
|
|
1886
|
+
]
|
|
1887
|
+
}
|
|
1888
|
+
),
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1890
|
+
"button",
|
|
1891
|
+
{
|
|
1892
|
+
type: "button",
|
|
1893
|
+
className: btnClass,
|
|
1894
|
+
onClick: () => openShareWindow(shareLinks.twitter, "twitter"),
|
|
1895
|
+
children: [
|
|
1896
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.MessageCircle, { className: "h-4 w-4" }),
|
|
1897
|
+
"Twitter"
|
|
1898
|
+
]
|
|
1899
|
+
}
|
|
1900
|
+
),
|
|
1901
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1902
|
+
"button",
|
|
1903
|
+
{
|
|
1904
|
+
type: "button",
|
|
1905
|
+
className: btnClass,
|
|
1906
|
+
onClick: () => openShareWindow(shareLinks.reddit, "reddit"),
|
|
1907
|
+
children: [
|
|
1908
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.FileText, { className: "h-4 w-4" }),
|
|
1909
|
+
"Reddit"
|
|
1910
|
+
]
|
|
1911
|
+
}
|
|
1912
|
+
),
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1914
|
+
"button",
|
|
1915
|
+
{
|
|
1916
|
+
type: "button",
|
|
1917
|
+
className: btnClass,
|
|
1918
|
+
onClick: () => openShareWindow(shareLinks.whatsapp, "whatsapp"),
|
|
1919
|
+
children: [
|
|
1920
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.MessageCircle, { className: "h-4 w-4" }),
|
|
1921
|
+
"WhatsApp"
|
|
1922
|
+
]
|
|
1923
|
+
}
|
|
1924
|
+
),
|
|
1925
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1926
|
+
"button",
|
|
1927
|
+
{
|
|
1928
|
+
type: "button",
|
|
1929
|
+
className: btnClass,
|
|
1930
|
+
onClick: () => openShareWindow(shareLinks.telegram, "telegram"),
|
|
1931
|
+
children: [
|
|
1932
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.MessageCircle, { className: "h-4 w-4" }),
|
|
1933
|
+
"Telegram"
|
|
1934
|
+
]
|
|
1935
|
+
}
|
|
1936
|
+
),
|
|
1937
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("a", { className: btnClass, href: shareLinks.email, onClick: () => trackShare("email"), children: [
|
|
1938
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Mail, { className: "h-4 w-4" }),
|
|
1406
1939
|
"Email"
|
|
1407
1940
|
] }),
|
|
1408
|
-
/* @__PURE__ */ (0,
|
|
1409
|
-
copied ? /* @__PURE__ */ (0,
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("button", { type: "button", className: btnClass, onClick: copyToClipboard, children: [
|
|
1942
|
+
copied ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Check, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Copy, { className: "h-4 w-4" }),
|
|
1410
1943
|
copied ? "Copied!" : "Copy Link"
|
|
1411
1944
|
] })
|
|
1412
1945
|
] }),
|
|
1413
|
-
shareMessage && /* @__PURE__ */ (0,
|
|
1946
|
+
shareMessage && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground mt-3", children: shareMessage })
|
|
1414
1947
|
] });
|
|
1415
1948
|
}
|
|
1416
1949
|
|
|
1417
1950
|
// src/ArticleNavigation.tsx
|
|
1418
|
-
var
|
|
1419
|
-
var
|
|
1420
|
-
var
|
|
1951
|
+
var import_link12 = __toESM(require("next/link"), 1);
|
|
1952
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1953
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1421
1954
|
function truncateTitle(title, maxLength = 60) {
|
|
1422
1955
|
return title.length > maxLength ? `${title.substring(0, maxLength)}...` : title;
|
|
1423
1956
|
}
|
|
1424
|
-
function ArticleNavigation({
|
|
1957
|
+
function ArticleNavigation({
|
|
1958
|
+
previous,
|
|
1959
|
+
next,
|
|
1960
|
+
basePath,
|
|
1961
|
+
fromSlug,
|
|
1962
|
+
pathKey,
|
|
1963
|
+
config
|
|
1964
|
+
}) {
|
|
1425
1965
|
if (!previous && !next) return null;
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1966
|
+
function emitStep(toSlug2, direction) {
|
|
1967
|
+
if (!pathKey || !fromSlug) return;
|
|
1968
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
1969
|
+
name: "path_step_advanced",
|
|
1970
|
+
pathKey,
|
|
1971
|
+
fromSlug,
|
|
1972
|
+
toSlug: toSlug2,
|
|
1973
|
+
direction
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("nav", { className: "mt-12 pt-8 border-t border-border", "aria-label": "Article navigation", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex justify-between gap-4", children: [
|
|
1977
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1 min-w-0", children: previous && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1978
|
+
import_link12.default,
|
|
1429
1979
|
{
|
|
1430
1980
|
href: `${basePath}/${previous.slug}`,
|
|
1981
|
+
onClick: () => emitStep(previous.slug, "previous"),
|
|
1431
1982
|
className: "group flex items-center gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors",
|
|
1432
1983
|
children: [
|
|
1433
|
-
/* @__PURE__ */ (0,
|
|
1434
|
-
/* @__PURE__ */ (0,
|
|
1435
|
-
/* @__PURE__ */ (0,
|
|
1436
|
-
/* @__PURE__ */ (0,
|
|
1984
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronLeft, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" }),
|
|
1985
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1986
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-sm text-muted-foreground mb-1", children: "Previous Article" }),
|
|
1987
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "font-medium text-foreground truncate", title: previous.title, children: truncateTitle(previous.title) })
|
|
1437
1988
|
] })
|
|
1438
1989
|
]
|
|
1439
1990
|
}
|
|
1440
1991
|
) }),
|
|
1441
|
-
/* @__PURE__ */ (0,
|
|
1442
|
-
|
|
1992
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1 min-w-0", children: next && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1993
|
+
import_link12.default,
|
|
1443
1994
|
{
|
|
1444
1995
|
href: `${basePath}/${next.slug}`,
|
|
1996
|
+
onClick: () => emitStep(next.slug, "next"),
|
|
1445
1997
|
className: "group flex items-center justify-end gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors",
|
|
1446
1998
|
children: [
|
|
1447
|
-
/* @__PURE__ */ (0,
|
|
1448
|
-
/* @__PURE__ */ (0,
|
|
1449
|
-
/* @__PURE__ */ (0,
|
|
1999
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1 text-right", children: [
|
|
2000
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-sm text-muted-foreground mb-1", children: "Next Article" }),
|
|
2001
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "font-medium text-foreground truncate", title: next.title, children: truncateTitle(next.title) })
|
|
1450
2002
|
] }),
|
|
1451
|
-
/* @__PURE__ */ (0,
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronRight, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" })
|
|
1452
2004
|
]
|
|
1453
2005
|
}
|
|
1454
2006
|
) })
|
|
1455
2007
|
] }) });
|
|
1456
2008
|
}
|
|
1457
2009
|
|
|
2010
|
+
// src/RelatedArticlesSection.tsx
|
|
2011
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
2012
|
+
function RelatedArticlesSection({
|
|
2013
|
+
articles,
|
|
2014
|
+
category,
|
|
2015
|
+
heading,
|
|
2016
|
+
config,
|
|
2017
|
+
fromSlug,
|
|
2018
|
+
source = "category"
|
|
2019
|
+
}) {
|
|
2020
|
+
var _a;
|
|
2021
|
+
if (articles.length === 0) return null;
|
|
2022
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("section", { className: "mt-12 pt-8 border-t border-border", "aria-label": "Related articles", children: [
|
|
2023
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2024
|
+
"h2",
|
|
2025
|
+
{
|
|
2026
|
+
className: "text-xl font-semibold text-foreground mb-6",
|
|
2027
|
+
style: { fontFamily: (_a = config == null ? void 0 : config.theme) == null ? void 0 : _a.headerFontFamily },
|
|
2028
|
+
children: heading != null ? heading : `More in ${category}`
|
|
2029
|
+
}
|
|
2030
|
+
),
|
|
2031
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "grid gap-6 sm:grid-cols-2 lg:grid-cols-3", children: articles.map((article) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
2032
|
+
"div",
|
|
2033
|
+
{
|
|
2034
|
+
onClickCapture: () => {
|
|
2035
|
+
if (!fromSlug) return;
|
|
2036
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
2037
|
+
name: "related_article_clicked",
|
|
2038
|
+
fromSlug,
|
|
2039
|
+
toSlug: article.slug,
|
|
2040
|
+
source
|
|
2041
|
+
});
|
|
2042
|
+
},
|
|
2043
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ArticleCard, { article, config })
|
|
2044
|
+
},
|
|
2045
|
+
article.slug
|
|
2046
|
+
)) })
|
|
2047
|
+
] });
|
|
2048
|
+
}
|
|
2049
|
+
|
|
1458
2050
|
// src/ArticleBackLink.tsx
|
|
1459
|
-
var
|
|
1460
|
-
var
|
|
1461
|
-
var
|
|
2051
|
+
var import_link13 = __toESM(require("next/link"), 1);
|
|
2052
|
+
var import_lucide_react10 = require("lucide-react");
|
|
2053
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1462
2054
|
function ArticleBackLink({
|
|
1463
2055
|
config,
|
|
1464
2056
|
href = "/articles",
|
|
@@ -1466,13 +2058,13 @@ function ArticleBackLink({
|
|
|
1466
2058
|
className
|
|
1467
2059
|
}) {
|
|
1468
2060
|
if (config.showBackToArticles === false) return null;
|
|
1469
|
-
return /* @__PURE__ */ (0,
|
|
1470
|
-
|
|
2061
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2062
|
+
import_link13.default,
|
|
1471
2063
|
{
|
|
1472
2064
|
href,
|
|
1473
2065
|
className: className != null ? className : "inline-flex items-center gap-2 mb-6 -ml-3 px-3 py-2 rounded-md text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors",
|
|
1474
2066
|
children: [
|
|
1475
|
-
/* @__PURE__ */ (0,
|
|
2067
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ArrowLeft, { className: "h-4 w-4" }),
|
|
1476
2068
|
label
|
|
1477
2069
|
]
|
|
1478
2070
|
}
|
|
@@ -1480,17 +2072,17 @@ function ArticleBackLink({
|
|
|
1480
2072
|
}
|
|
1481
2073
|
|
|
1482
2074
|
// src/ArticleTOC.tsx
|
|
1483
|
-
var
|
|
2075
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1484
2076
|
function ArticleTOC({ toc, className }) {
|
|
1485
2077
|
if (!toc.length) return null;
|
|
1486
|
-
return /* @__PURE__ */ (0,
|
|
2078
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1487
2079
|
"nav",
|
|
1488
2080
|
{
|
|
1489
2081
|
"aria-label": "Table of contents",
|
|
1490
2082
|
className: `mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
|
|
1491
2083
|
children: [
|
|
1492
|
-
/* @__PURE__ */ (0,
|
|
1493
|
-
/* @__PURE__ */ (0,
|
|
2084
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
|
|
2085
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("ul", { className: "space-y-1 text-sm", children: toc.map((item) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("li", { style: { paddingLeft: `${Math.max(0, item.depth - 2) * 1}rem` }, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1494
2086
|
"a",
|
|
1495
2087
|
{
|
|
1496
2088
|
href: `#${item.id}`,
|
|
@@ -1504,35 +2096,35 @@ function ArticleTOC({ toc, className }) {
|
|
|
1504
2096
|
}
|
|
1505
2097
|
|
|
1506
2098
|
// src/ScrollToTop.tsx
|
|
1507
|
-
var
|
|
1508
|
-
var
|
|
1509
|
-
var
|
|
2099
|
+
var import_react7 = require("react");
|
|
2100
|
+
var import_lucide_react11 = require("lucide-react");
|
|
2101
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1510
2102
|
function ScrollToTop() {
|
|
1511
|
-
const [visible, setVisible] = (0,
|
|
1512
|
-
(0,
|
|
2103
|
+
const [visible, setVisible] = (0, import_react7.useState)(false);
|
|
2104
|
+
(0, import_react7.useEffect)(() => {
|
|
1513
2105
|
const onScroll = () => setVisible(globalThis.scrollY > 300);
|
|
1514
2106
|
globalThis.addEventListener("scroll", onScroll, { passive: true });
|
|
1515
2107
|
return () => globalThis.removeEventListener("scroll", onScroll);
|
|
1516
2108
|
}, []);
|
|
1517
2109
|
if (!visible) return null;
|
|
1518
|
-
return /* @__PURE__ */ (0,
|
|
2110
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1519
2111
|
"button",
|
|
1520
2112
|
{
|
|
1521
2113
|
"aria-label": "Scroll to top",
|
|
1522
2114
|
onClick: () => globalThis.scrollTo({ top: 0, behavior: "smooth" }),
|
|
1523
2115
|
className: "fixed bottom-8 right-8 z-50 rounded-full bg-primary p-2 shadow-md text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
1524
|
-
children: /* @__PURE__ */ (0,
|
|
2116
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react11.ArrowUp, { className: "h-5 w-5" })
|
|
1525
2117
|
}
|
|
1526
2118
|
);
|
|
1527
2119
|
}
|
|
1528
2120
|
|
|
1529
2121
|
// src/CommentsSection.tsx
|
|
1530
|
-
var
|
|
1531
|
-
var
|
|
2122
|
+
var import_react10 = require("next-auth/react");
|
|
2123
|
+
var import_react11 = require("react");
|
|
1532
2124
|
|
|
1533
2125
|
// src/CommentForm.tsx
|
|
1534
|
-
var
|
|
1535
|
-
var
|
|
2126
|
+
var import_react8 = require("react");
|
|
2127
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1536
2128
|
var MAX_LENGTH = 2e3;
|
|
1537
2129
|
var WARN_THRESHOLD = 1800;
|
|
1538
2130
|
function submitLabel(submitting, parentId) {
|
|
@@ -1540,9 +2132,9 @@ function submitLabel(submitting, parentId) {
|
|
|
1540
2132
|
return parentId ? "Reply" : "Post comment";
|
|
1541
2133
|
}
|
|
1542
2134
|
function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
1543
|
-
const [body, setBody] = (0,
|
|
1544
|
-
const [submitting, setSubmitting] = (0,
|
|
1545
|
-
const [error, setError] = (0,
|
|
2135
|
+
const [body, setBody] = (0, import_react8.useState)("");
|
|
2136
|
+
const [submitting, setSubmitting] = (0, import_react8.useState)(false);
|
|
2137
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
1546
2138
|
const remaining = MAX_LENGTH - body.length;
|
|
1547
2139
|
function handleSubmit(e) {
|
|
1548
2140
|
return __async(this, null, function* () {
|
|
@@ -1571,8 +2163,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1571
2163
|
}
|
|
1572
2164
|
});
|
|
1573
2165
|
}
|
|
1574
|
-
return /* @__PURE__ */ (0,
|
|
1575
|
-
/* @__PURE__ */ (0,
|
|
2166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
|
|
2167
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1576
2168
|
"textarea",
|
|
1577
2169
|
{
|
|
1578
2170
|
value: body,
|
|
@@ -1585,13 +2177,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1585
2177
|
"aria-label": parentId ? "Reply text" : "Comment text"
|
|
1586
2178
|
}
|
|
1587
2179
|
),
|
|
1588
|
-
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0,
|
|
2180
|
+
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
1589
2181
|
remaining,
|
|
1590
2182
|
" characters remaining"
|
|
1591
2183
|
] }),
|
|
1592
|
-
error && /* @__PURE__ */ (0,
|
|
1593
|
-
/* @__PURE__ */ (0,
|
|
1594
|
-
/* @__PURE__ */ (0,
|
|
2184
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-xs text-destructive", children: error }),
|
|
2185
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex gap-2", children: [
|
|
2186
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1595
2187
|
"button",
|
|
1596
2188
|
{
|
|
1597
2189
|
type: "submit",
|
|
@@ -1600,7 +2192,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1600
2192
|
children: submitLabel(submitting, parentId)
|
|
1601
2193
|
}
|
|
1602
2194
|
),
|
|
1603
|
-
onCancel && /* @__PURE__ */ (0,
|
|
2195
|
+
onCancel && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1604
2196
|
"button",
|
|
1605
2197
|
{
|
|
1606
2198
|
type: "button",
|
|
@@ -1614,8 +2206,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1614
2206
|
}
|
|
1615
2207
|
|
|
1616
2208
|
// src/CommentItem.tsx
|
|
1617
|
-
var
|
|
1618
|
-
var
|
|
2209
|
+
var import_react9 = require("react");
|
|
2210
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1619
2211
|
function relativeTime(iso) {
|
|
1620
2212
|
const diff = Date.now() - new Date(iso).getTime();
|
|
1621
2213
|
const minutes = Math.floor(diff / 6e4);
|
|
@@ -1636,8 +2228,8 @@ function CommentItem({
|
|
|
1636
2228
|
isReply = false,
|
|
1637
2229
|
onRefresh
|
|
1638
2230
|
}) {
|
|
1639
|
-
const [showReplyForm, setShowReplyForm] = (0,
|
|
1640
|
-
const [deleting, setDeleting] = (0,
|
|
2231
|
+
const [showReplyForm, setShowReplyForm] = (0, import_react9.useState)(false);
|
|
2232
|
+
const [deleting, setDeleting] = (0, import_react9.useState)(false);
|
|
1641
2233
|
const replies = "replies" in comment ? comment.replies : [];
|
|
1642
2234
|
const canReply = !isReply && !!currentUserId && !comment.isDeleted;
|
|
1643
2235
|
const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted;
|
|
@@ -1653,15 +2245,15 @@ function CommentItem({
|
|
|
1653
2245
|
}
|
|
1654
2246
|
});
|
|
1655
2247
|
}
|
|
1656
|
-
return /* @__PURE__ */ (0,
|
|
1657
|
-
/* @__PURE__ */ (0,
|
|
1658
|
-
/* @__PURE__ */ (0,
|
|
1659
|
-
/* @__PURE__ */ (0,
|
|
1660
|
-
/* @__PURE__ */ (0,
|
|
2248
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
|
|
2249
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
2250
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2251
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
|
|
2252
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
|
|
1661
2253
|
] }),
|
|
1662
|
-
/* @__PURE__ */ (0,
|
|
1663
|
-
/* @__PURE__ */ (0,
|
|
1664
|
-
canReply && /* @__PURE__ */ (0,
|
|
2254
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
|
|
2255
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex gap-3 pt-1", children: [
|
|
2256
|
+
canReply && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1665
2257
|
"button",
|
|
1666
2258
|
{
|
|
1667
2259
|
onClick: () => setShowReplyForm((v) => !v),
|
|
@@ -1669,7 +2261,7 @@ function CommentItem({
|
|
|
1669
2261
|
children: showReplyForm ? "Cancel" : "Reply"
|
|
1670
2262
|
}
|
|
1671
2263
|
),
|
|
1672
|
-
canDelete && /* @__PURE__ */ (0,
|
|
2264
|
+
canDelete && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1673
2265
|
"button",
|
|
1674
2266
|
{
|
|
1675
2267
|
onClick: handleDelete,
|
|
@@ -1680,7 +2272,7 @@ function CommentItem({
|
|
|
1680
2272
|
)
|
|
1681
2273
|
] })
|
|
1682
2274
|
] }) }),
|
|
1683
|
-
showReplyForm && /* @__PURE__ */ (0,
|
|
2275
|
+
showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1684
2276
|
CommentForm,
|
|
1685
2277
|
{
|
|
1686
2278
|
articleSlug,
|
|
@@ -1692,7 +2284,7 @@ function CommentItem({
|
|
|
1692
2284
|
onCancel: () => setShowReplyForm(false)
|
|
1693
2285
|
}
|
|
1694
2286
|
) }),
|
|
1695
|
-
replies.length > 0 && /* @__PURE__ */ (0,
|
|
2287
|
+
replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1696
2288
|
CommentItem,
|
|
1697
2289
|
{
|
|
1698
2290
|
comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
|
|
@@ -1707,7 +2299,7 @@ function CommentItem({
|
|
|
1707
2299
|
}
|
|
1708
2300
|
|
|
1709
2301
|
// src/CommentThread.tsx
|
|
1710
|
-
var
|
|
2302
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1711
2303
|
function CommentThread({
|
|
1712
2304
|
comments,
|
|
1713
2305
|
articleSlug,
|
|
@@ -1715,9 +2307,9 @@ function CommentThread({
|
|
|
1715
2307
|
onRefresh
|
|
1716
2308
|
}) {
|
|
1717
2309
|
if (comments.length === 0) {
|
|
1718
|
-
return /* @__PURE__ */ (0,
|
|
2310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
|
|
1719
2311
|
}
|
|
1720
|
-
return /* @__PURE__ */ (0,
|
|
2312
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1721
2313
|
CommentItem,
|
|
1722
2314
|
{
|
|
1723
2315
|
comment,
|
|
@@ -1730,17 +2322,17 @@ function CommentThread({
|
|
|
1730
2322
|
}
|
|
1731
2323
|
|
|
1732
2324
|
// src/CommentsSection.tsx
|
|
1733
|
-
var
|
|
2325
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1734
2326
|
function CommentsSection({ articleSlug, config }) {
|
|
1735
2327
|
var _a, _b, _c, _d, _e, _f;
|
|
1736
|
-
const { data: session, status } = (0,
|
|
1737
|
-
const [comments, setComments] = (0,
|
|
1738
|
-
const [loading, setLoading] = (0,
|
|
1739
|
-
const [fetchError, setFetchError] = (0,
|
|
2328
|
+
const { data: session, status } = (0, import_react10.useSession)();
|
|
2329
|
+
const [comments, setComments] = (0, import_react11.useState)([]);
|
|
2330
|
+
const [loading, setLoading] = (0, import_react11.useState)(true);
|
|
2331
|
+
const [fetchError, setFetchError] = (0, import_react11.useState)(null);
|
|
1740
2332
|
const perArticleEnabled = (_b = (_a = config.perArticleOverride) == null ? void 0 : _a[articleSlug]) != null ? _b : true;
|
|
1741
2333
|
const enabled = config.enabled && perArticleEnabled;
|
|
1742
2334
|
const currentUserId = (_f = (_e = (_c = session == null ? void 0 : session.user) == null ? void 0 : _c.email) != null ? _e : (_d = session == null ? void 0 : session.user) == null ? void 0 : _d.name) != null ? _f : void 0;
|
|
1743
|
-
const fetchComments = (0,
|
|
2335
|
+
const fetchComments = (0, import_react11.useCallback)(() => __async(this, null, function* () {
|
|
1744
2336
|
setLoading(true);
|
|
1745
2337
|
setFetchError(null);
|
|
1746
2338
|
try {
|
|
@@ -1754,7 +2346,7 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1754
2346
|
setLoading(false);
|
|
1755
2347
|
}
|
|
1756
2348
|
}), [articleSlug]);
|
|
1757
|
-
(0,
|
|
2349
|
+
(0, import_react11.useEffect)(() => {
|
|
1758
2350
|
if (enabled) {
|
|
1759
2351
|
fetchComments().catch(() => void 0);
|
|
1760
2352
|
}
|
|
@@ -1762,13 +2354,13 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1762
2354
|
if (!enabled) return null;
|
|
1763
2355
|
const count = comments.length;
|
|
1764
2356
|
const label = count === 1 ? "1 comment" : `${count} comments`;
|
|
1765
|
-
return /* @__PURE__ */ (0,
|
|
1766
|
-
/* @__PURE__ */ (0,
|
|
1767
|
-
status === "authenticated" ? /* @__PURE__ */ (0,
|
|
1768
|
-
/* @__PURE__ */ (0,
|
|
2357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
|
|
2358
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
|
|
2359
|
+
status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
2360
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1769
2361
|
"button",
|
|
1770
2362
|
{
|
|
1771
|
-
onClick: () => (0,
|
|
2363
|
+
onClick: () => (0, import_react10.signIn)(),
|
|
1772
2364
|
className: "text-primary underline hover:no-underline focus:outline-none",
|
|
1773
2365
|
children: "Sign in"
|
|
1774
2366
|
}
|
|
@@ -1776,8 +2368,8 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1776
2368
|
" ",
|
|
1777
2369
|
"to join the discussion."
|
|
1778
2370
|
] }),
|
|
1779
|
-
fetchError && /* @__PURE__ */ (0,
|
|
1780
|
-
loading ? /* @__PURE__ */ (0,
|
|
2371
|
+
fetchError && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
|
|
2372
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1781
2373
|
CommentThread,
|
|
1782
2374
|
{
|
|
1783
2375
|
comments,
|
|
@@ -1796,10 +2388,10 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1796
2388
|
ArticleDetailHero,
|
|
1797
2389
|
ArticleNavigation,
|
|
1798
2390
|
ArticleSEO,
|
|
1799
|
-
ArticleSchema,
|
|
1800
2391
|
ArticleSearchBar,
|
|
1801
2392
|
ArticleSocialShare,
|
|
1802
2393
|
ArticleTOC,
|
|
2394
|
+
ArticleViewTracker,
|
|
1803
2395
|
ArticlesHero,
|
|
1804
2396
|
ArticlesPage,
|
|
1805
2397
|
AuthorArticlesPage,
|
|
@@ -1814,6 +2406,7 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1814
2406
|
CommentItem,
|
|
1815
2407
|
CommentThread,
|
|
1816
2408
|
CommentsSection,
|
|
2409
|
+
CtaViewTracker,
|
|
1817
2410
|
DEFAULT_CATEGORIES_PAGE_SIZE,
|
|
1818
2411
|
DEFAULT_LAYOUT,
|
|
1819
2412
|
DEFAULT_PAGE_SIZE,
|
|
@@ -1821,7 +2414,10 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1821
2414
|
FeaturedArticle,
|
|
1822
2415
|
LatestArticles,
|
|
1823
2416
|
LatestArticlesSection,
|
|
2417
|
+
PaginationNav,
|
|
2418
|
+
RelatedArticlesSection,
|
|
1824
2419
|
ScrollToTop,
|
|
2420
|
+
SeriesArticlesPage,
|
|
1825
2421
|
breadcrumbsAreEnabled,
|
|
1826
2422
|
getBreadcrumbsConfig,
|
|
1827
2423
|
useArticles
|