@fullstackdatasolutions/articles 0.12.0 → 1.0.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 +24 -0
- package/README.md +550 -3
- package/dist/index.cjs +960 -383
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +372 -20
- package/dist/index.d.ts +372 -20
- package/dist/index.js +944 -371
- 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 +141 -0
- package/dist/nextjs.d.ts +141 -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 +349 -3
- package/dist/server.d.ts +349 -3
- package/dist/server.js +643 -29
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/ArticleCard.tsx +37 -1
- package/src/ArticleContent.tsx +144 -5
- package/src/ArticleDetailHero.tsx +23 -0
- package/src/ArticleNavigation.tsx +32 -1
- package/src/ArticleSchemas.tsx +43 -39
- package/src/ArticleSocialShare.tsx +54 -10
- package/src/ArticlesPage.tsx +55 -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 +55 -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 +37 -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,98 @@ 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
|
+
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: [
|
|
654
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "p-0", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
655
|
+
import_image4.default,
|
|
587
656
|
{
|
|
588
657
|
src: article.featuredImage,
|
|
589
658
|
alt: article.title,
|
|
@@ -592,35 +661,67 @@ function ArticleCard({ article }) {
|
|
|
592
661
|
height: 200
|
|
593
662
|
}
|
|
594
663
|
) }),
|
|
595
|
-
/* @__PURE__ */ (0,
|
|
596
|
-
/* @__PURE__ */ (0,
|
|
597
|
-
/* @__PURE__ */ (0,
|
|
598
|
-
|
|
664
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-6", children: [
|
|
665
|
+
/* @__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 }) }),
|
|
666
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "text-lg font-semibold leading-none tracking-tight mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
667
|
+
import_link6.default,
|
|
599
668
|
{
|
|
600
669
|
href: `/articles/${article.slug}`,
|
|
601
670
|
className: "hover:text-indigo-600 transition-colors",
|
|
602
671
|
children: article.title
|
|
603
672
|
}
|
|
604
673
|
) }),
|
|
605
|
-
/* @__PURE__ */ (0,
|
|
606
|
-
/* @__PURE__ */ (0,
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
674
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-muted-foreground mb-4 line-clamp-3", children: article.excerpt }),
|
|
675
|
+
article.authorSlug && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
676
|
+
import_link6.default,
|
|
677
|
+
{
|
|
678
|
+
href: `/articles/authors/${article.authorSlug}`,
|
|
679
|
+
className: "mb-4 flex items-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors",
|
|
680
|
+
onClick: () => emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
681
|
+
name: "author_clicked",
|
|
682
|
+
articleSlug: article.slug,
|
|
683
|
+
authorSlug: article.authorSlug
|
|
684
|
+
}),
|
|
685
|
+
children: [
|
|
686
|
+
article.authorAvatar ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
687
|
+
import_image4.default,
|
|
688
|
+
{
|
|
689
|
+
src: article.authorAvatar,
|
|
690
|
+
alt: "",
|
|
691
|
+
width: 24,
|
|
692
|
+
height: 24,
|
|
693
|
+
className: "h-6 w-6 rounded-full object-cover"
|
|
694
|
+
}
|
|
695
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
696
|
+
"span",
|
|
697
|
+
{
|
|
698
|
+
"aria-hidden": "true",
|
|
699
|
+
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground",
|
|
700
|
+
children: getInitials(article.author)
|
|
701
|
+
}
|
|
702
|
+
),
|
|
703
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: article.author })
|
|
704
|
+
]
|
|
705
|
+
}
|
|
706
|
+
),
|
|
707
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between text-sm text-muted-foreground border-t pt-4", children: [
|
|
708
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
709
|
+
article.date && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
710
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Calendar, { className: "h-3 w-3" }),
|
|
711
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: article.date })
|
|
611
712
|
] }),
|
|
612
|
-
/* @__PURE__ */ (0,
|
|
613
|
-
/* @__PURE__ */ (0,
|
|
614
|
-
/* @__PURE__ */ (0,
|
|
713
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
714
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Clock, { className: "h-3 w-3" }),
|
|
715
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: article.readTime })
|
|
615
716
|
] })
|
|
616
717
|
] }),
|
|
617
|
-
/* @__PURE__ */ (0,
|
|
618
|
-
|
|
718
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
719
|
+
import_link6.default,
|
|
619
720
|
{
|
|
620
721
|
href: `/articles/${article.slug}`,
|
|
621
722
|
className: "inline-flex items-center justify-center h-8 w-8 p-0 rounded-md hover:bg-accent transition-colors",
|
|
622
723
|
"aria-label": `Read ${article.title}`,
|
|
623
|
-
children: /* @__PURE__ */ (0,
|
|
724
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.ArrowRight, { className: "h-4 w-4" })
|
|
624
725
|
}
|
|
625
726
|
)
|
|
626
727
|
] })
|
|
@@ -628,18 +729,114 @@ function ArticleCard({ article }) {
|
|
|
628
729
|
] });
|
|
629
730
|
}
|
|
630
731
|
|
|
732
|
+
// src/PaginationNav.tsx
|
|
733
|
+
var import_link7 = __toESM(require("next/link"), 1);
|
|
734
|
+
var import_lucide_react5 = require("lucide-react");
|
|
735
|
+
|
|
736
|
+
// src/pagination.ts
|
|
737
|
+
function buildPageUrl(basePath, page) {
|
|
738
|
+
const base = basePath.replace(/\/$/, "");
|
|
739
|
+
return page > 1 ? `${base}/page/${page}` : base;
|
|
740
|
+
}
|
|
741
|
+
function buildPaginationLinks(basePath, page, totalPages) {
|
|
742
|
+
return {
|
|
743
|
+
canonicalUrl: buildPageUrl(basePath, page),
|
|
744
|
+
prevUrl: page > 1 ? buildPageUrl(basePath, page - 1) : null,
|
|
745
|
+
nextUrl: page < totalPages ? buildPageUrl(basePath, page + 1) : null
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// src/PaginationNav.tsx
|
|
750
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
751
|
+
function PaginationNav({ basePath, page, totalPages }) {
|
|
752
|
+
if (totalPages <= 1) return null;
|
|
753
|
+
const { prevUrl, nextUrl } = buildPaginationLinks(basePath, page, totalPages);
|
|
754
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
755
|
+
"nav",
|
|
756
|
+
{
|
|
757
|
+
className: "mt-10 flex items-center justify-center gap-4",
|
|
758
|
+
"aria-label": "Article listing pagination",
|
|
759
|
+
children: [
|
|
760
|
+
prevUrl && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("link", { rel: "prev", href: prevUrl }),
|
|
761
|
+
nextUrl && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("link", { rel: "next", href: nextUrl }),
|
|
762
|
+
prevUrl ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
763
|
+
import_link7.default,
|
|
764
|
+
{
|
|
765
|
+
href: prevUrl,
|
|
766
|
+
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",
|
|
767
|
+
children: [
|
|
768
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.ChevronLeft, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
769
|
+
"Previous"
|
|
770
|
+
]
|
|
771
|
+
}
|
|
772
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
773
|
+
"span",
|
|
774
|
+
{
|
|
775
|
+
"aria-hidden": "true",
|
|
776
|
+
className: "inline-flex items-center gap-1 px-4 py-2 rounded-md font-medium text-sm text-muted-foreground opacity-50",
|
|
777
|
+
children: [
|
|
778
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.ChevronLeft, { className: "h-4 w-4" }),
|
|
779
|
+
"Previous"
|
|
780
|
+
]
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-sm text-muted-foreground", children: [
|
|
784
|
+
"Page ",
|
|
785
|
+
page,
|
|
786
|
+
" of ",
|
|
787
|
+
totalPages
|
|
788
|
+
] }),
|
|
789
|
+
nextUrl ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
790
|
+
import_link7.default,
|
|
791
|
+
{
|
|
792
|
+
href: nextUrl,
|
|
793
|
+
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",
|
|
794
|
+
children: [
|
|
795
|
+
"Next",
|
|
796
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.ChevronRight, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
797
|
+
]
|
|
798
|
+
}
|
|
799
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
800
|
+
"span",
|
|
801
|
+
{
|
|
802
|
+
"aria-hidden": "true",
|
|
803
|
+
className: "inline-flex items-center gap-1 px-4 py-2 rounded-md font-medium text-sm text-muted-foreground opacity-50",
|
|
804
|
+
children: [
|
|
805
|
+
"Next",
|
|
806
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react5.ChevronRight, { className: "h-4 w-4" })
|
|
807
|
+
]
|
|
808
|
+
}
|
|
809
|
+
)
|
|
810
|
+
]
|
|
811
|
+
}
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
|
|
631
815
|
// src/LatestArticles.tsx
|
|
632
|
-
var
|
|
633
|
-
function LatestArticles({ articles, pageSize }) {
|
|
816
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
817
|
+
function LatestArticles({ articles, pageSize, pagination }) {
|
|
634
818
|
const [visibleCount, setVisibleCount] = (0, import_react2.useState)(pageSize);
|
|
635
819
|
(0, import_react2.useEffect)(() => {
|
|
636
820
|
setVisibleCount(pageSize);
|
|
637
821
|
}, [articles, pageSize]);
|
|
822
|
+
if (pagination) {
|
|
823
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
824
|
+
/* @__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)) }),
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
826
|
+
PaginationNav,
|
|
827
|
+
{
|
|
828
|
+
basePath: pagination.basePath,
|
|
829
|
+
page: pagination.page,
|
|
830
|
+
totalPages: pagination.totalPages
|
|
831
|
+
}
|
|
832
|
+
)
|
|
833
|
+
] });
|
|
834
|
+
}
|
|
638
835
|
const visible = articles.slice(0, visibleCount);
|
|
639
836
|
const hasMore = visibleCount < articles.length;
|
|
640
|
-
return /* @__PURE__ */ (0,
|
|
641
|
-
/* @__PURE__ */ (0,
|
|
642
|
-
hasMore && /* @__PURE__ */ (0,
|
|
837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
838
|
+
/* @__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)) }),
|
|
839
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
643
840
|
"button",
|
|
644
841
|
{
|
|
645
842
|
type: "button",
|
|
@@ -652,35 +849,36 @@ function LatestArticles({ articles, pageSize }) {
|
|
|
652
849
|
}
|
|
653
850
|
|
|
654
851
|
// src/LatestArticlesSection.tsx
|
|
655
|
-
var
|
|
852
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
656
853
|
function DescriptionText({
|
|
657
854
|
searchQuery,
|
|
658
855
|
count
|
|
659
856
|
}) {
|
|
660
857
|
const pluralSuffix = count === 1 ? "" : "s";
|
|
661
858
|
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,
|
|
859
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-lg text-muted-foreground", children: text });
|
|
663
860
|
}
|
|
664
861
|
function LatestArticlesSection({
|
|
665
862
|
articles,
|
|
666
863
|
searchQuery,
|
|
667
864
|
onClearSearch,
|
|
668
|
-
pageSize = 6
|
|
865
|
+
pageSize = 6,
|
|
866
|
+
pagination
|
|
669
867
|
}) {
|
|
670
|
-
return /* @__PURE__ */ (0,
|
|
671
|
-
/* @__PURE__ */ (0,
|
|
672
|
-
/* @__PURE__ */ (0,
|
|
673
|
-
/* @__PURE__ */ (0,
|
|
868
|
+
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: [
|
|
869
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-center mb-12", children: [
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "text-3xl font-bold text-foreground mb-4", children: searchQuery ? "Search Results" : "Latest Articles" }),
|
|
871
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DescriptionText, { searchQuery, count: articles.length })
|
|
674
872
|
] }),
|
|
675
|
-
articles.length === 0 && searchQuery ? /* @__PURE__ */ (0,
|
|
676
|
-
/* @__PURE__ */ (0,
|
|
677
|
-
/* @__PURE__ */ (0,
|
|
678
|
-
/* @__PURE__ */ (0,
|
|
873
|
+
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: [
|
|
874
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react6.Search, { className: "h-12 w-12 text-muted-foreground mx-auto mb-4" }),
|
|
875
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-lg font-semibold text-foreground mb-2", children: "No articles found" }),
|
|
876
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-muted-foreground mb-4", children: [
|
|
679
877
|
`We couldn't find any articles matching "`,
|
|
680
878
|
searchQuery,
|
|
681
879
|
'". Try adjusting your search terms.'
|
|
682
880
|
] }),
|
|
683
|
-
/* @__PURE__ */ (0,
|
|
881
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
684
882
|
"button",
|
|
685
883
|
{
|
|
686
884
|
type: "button",
|
|
@@ -689,7 +887,15 @@ function LatestArticlesSection({
|
|
|
689
887
|
children: "Clear search"
|
|
690
888
|
}
|
|
691
889
|
)
|
|
692
|
-
] }) }) : /* @__PURE__ */ (0,
|
|
890
|
+
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
891
|
+
LatestArticles,
|
|
892
|
+
{
|
|
893
|
+
articles,
|
|
894
|
+
pageSize,
|
|
895
|
+
pagination: searchQuery ? void 0 : pagination
|
|
896
|
+
},
|
|
897
|
+
searchQuery
|
|
898
|
+
)
|
|
693
899
|
] }) });
|
|
694
900
|
}
|
|
695
901
|
|
|
@@ -782,7 +988,7 @@ function useArticles(initialArticles, initialCategories) {
|
|
|
782
988
|
}
|
|
783
989
|
|
|
784
990
|
// src/ArticlesPage.tsx
|
|
785
|
-
var
|
|
991
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
786
992
|
function buildThemeVars(theme) {
|
|
787
993
|
if (!theme) return {};
|
|
788
994
|
const vars = {};
|
|
@@ -811,11 +1017,12 @@ function renderSection(section, ctx, config) {
|
|
|
811
1017
|
error,
|
|
812
1018
|
handleSearch,
|
|
813
1019
|
pageSize,
|
|
814
|
-
categoriesPageSize
|
|
1020
|
+
categoriesPageSize,
|
|
1021
|
+
pagination
|
|
815
1022
|
} = ctx;
|
|
816
1023
|
switch (section) {
|
|
817
1024
|
case "hero":
|
|
818
|
-
return /* @__PURE__ */ (0,
|
|
1025
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
819
1026
|
ArticlesHero,
|
|
820
1027
|
{
|
|
821
1028
|
title: (_a = config.hero) == null ? void 0 : _a.title,
|
|
@@ -824,7 +1031,7 @@ function renderSection(section, ctx, config) {
|
|
|
824
1031
|
"hero"
|
|
825
1032
|
);
|
|
826
1033
|
case "search":
|
|
827
|
-
return /* @__PURE__ */ (0,
|
|
1034
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
828
1035
|
ArticleSearchBar,
|
|
829
1036
|
{
|
|
830
1037
|
value: searchQuery,
|
|
@@ -835,7 +1042,7 @@ function renderSection(section, ctx, config) {
|
|
|
835
1042
|
"search"
|
|
836
1043
|
);
|
|
837
1044
|
case "featured":
|
|
838
|
-
return articles.length > 0 && !searchQuery ? /* @__PURE__ */ (0,
|
|
1045
|
+
return articles.length > 0 && !searchQuery ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
839
1046
|
FeaturedArticle,
|
|
840
1047
|
{
|
|
841
1048
|
article: articles[0],
|
|
@@ -845,16 +1052,16 @@ function renderSection(section, ctx, config) {
|
|
|
845
1052
|
) : null;
|
|
846
1053
|
case "latest":
|
|
847
1054
|
if (loading && articles.length === 0) {
|
|
848
|
-
return /* @__PURE__ */ (0,
|
|
849
|
-
/* @__PURE__ */ (0,
|
|
850
|
-
/* @__PURE__ */ (0,
|
|
1055
|
+
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: [
|
|
1056
|
+
/* @__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" }),
|
|
1057
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-muted-foreground", children: "Loading articles..." })
|
|
851
1058
|
] }) }) }, "latest");
|
|
852
1059
|
}
|
|
853
1060
|
if (error) {
|
|
854
|
-
return /* @__PURE__ */ (0,
|
|
855
|
-
/* @__PURE__ */ (0,
|
|
856
|
-
/* @__PURE__ */ (0,
|
|
857
|
-
/* @__PURE__ */ (0,
|
|
1061
|
+
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: [
|
|
1062
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold text-red-800 mb-2", children: "Error Loading Articles" }),
|
|
1063
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-red-600 mb-4", children: error }),
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
858
1065
|
"button",
|
|
859
1066
|
{
|
|
860
1067
|
type: "button",
|
|
@@ -865,18 +1072,19 @@ function renderSection(section, ctx, config) {
|
|
|
865
1072
|
)
|
|
866
1073
|
] }) }) }, "latest");
|
|
867
1074
|
}
|
|
868
|
-
return /* @__PURE__ */ (0,
|
|
1075
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
869
1076
|
LatestArticlesSection,
|
|
870
1077
|
{
|
|
871
1078
|
articles: displayedArticles,
|
|
872
1079
|
searchQuery,
|
|
873
1080
|
onClearSearch: () => handleSearch(""),
|
|
874
|
-
pageSize
|
|
1081
|
+
pageSize,
|
|
1082
|
+
pagination
|
|
875
1083
|
},
|
|
876
1084
|
"latest"
|
|
877
1085
|
);
|
|
878
1086
|
case "categories":
|
|
879
|
-
return searchQuery ? null : /* @__PURE__ */ (0,
|
|
1087
|
+
return searchQuery ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
880
1088
|
ArticleCategoryGrid,
|
|
881
1089
|
{
|
|
882
1090
|
categories,
|
|
@@ -892,7 +1100,10 @@ function renderSection(section, ctx, config) {
|
|
|
892
1100
|
function ArticlesPage({
|
|
893
1101
|
config,
|
|
894
1102
|
initialArticles,
|
|
895
|
-
initialCategories
|
|
1103
|
+
initialCategories,
|
|
1104
|
+
page,
|
|
1105
|
+
totalPages,
|
|
1106
|
+
totalCount
|
|
896
1107
|
}) {
|
|
897
1108
|
var _a, _b, _c;
|
|
898
1109
|
const state = useArticles(initialArticles, initialCategories);
|
|
@@ -900,19 +1111,34 @@ function ArticlesPage({
|
|
|
900
1111
|
const pageSize = (_b = config.pageSize) != null ? _b : DEFAULT_PAGE_SIZE;
|
|
901
1112
|
const categoriesPageSize = (_c = config.categoriesPageSize) != null ? _c : DEFAULT_CATEGORIES_PAGE_SIZE;
|
|
902
1113
|
const themeVars = buildThemeVars(config.theme);
|
|
903
|
-
const
|
|
1114
|
+
const isPagesMode = config.listingPagination === "pages" && page !== void 0 && totalPages !== void 0;
|
|
1115
|
+
const currentPage = page != null ? page : 1;
|
|
1116
|
+
const effectiveLayout = isPagesMode && currentPage > 1 ? layout.filter((section) => section !== "featured") : layout;
|
|
1117
|
+
const hasFeatured = effectiveLayout.includes("featured");
|
|
904
1118
|
const articlesWithoutFeatured = hasFeatured ? state.articles.slice(1) : state.articles;
|
|
905
1119
|
const displayedArticles = state.searchQuery ? state.articles : articlesWithoutFeatured;
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1120
|
+
const pagination = isPagesMode ? { page: currentPage, totalPages, basePath: "/articles" } : void 0;
|
|
1121
|
+
const ctx = __spreadProps(__spreadValues({}, state), {
|
|
1122
|
+
displayedArticles,
|
|
1123
|
+
pageSize,
|
|
1124
|
+
categoriesPageSize,
|
|
1125
|
+
pagination
|
|
1126
|
+
});
|
|
1127
|
+
const articleCount = totalCount != null ? totalCount : state.articles.length;
|
|
1128
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: themeVars, children: [
|
|
1129
|
+
effectiveLayout.map((section) => renderSection(section, ctx, config)),
|
|
1130
|
+
state.articles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
910
1131
|
CollectionPageSchema,
|
|
911
1132
|
{
|
|
912
1133
|
title: `Articles | ${config.siteName}`,
|
|
913
|
-
description: `Browse all ${
|
|
1134
|
+
description: `Browse all ${articleCount} articles on ${config.siteName}.`,
|
|
914
1135
|
url: `${config.siteUrl}/articles`,
|
|
915
|
-
articleCount
|
|
1136
|
+
articleCount,
|
|
1137
|
+
items: displayedArticles.slice(0, pageSize).map((article, index) => ({
|
|
1138
|
+
position: index + 1,
|
|
1139
|
+
url: `${config.siteUrl}/articles/${article.slug}`,
|
|
1140
|
+
name: article.title
|
|
1141
|
+
}))
|
|
916
1142
|
}
|
|
917
1143
|
)
|
|
918
1144
|
] });
|
|
@@ -920,10 +1146,10 @@ function ArticlesPage({
|
|
|
920
1146
|
|
|
921
1147
|
// src/ArticleDetailHero.tsx
|
|
922
1148
|
var import_react4 = require("react");
|
|
923
|
-
var
|
|
924
|
-
var
|
|
925
|
-
var
|
|
926
|
-
var
|
|
1149
|
+
var import_image5 = __toESM(require("next/image"), 1);
|
|
1150
|
+
var import_link8 = __toESM(require("next/link"), 1);
|
|
1151
|
+
var import_lucide_react7 = require("lucide-react");
|
|
1152
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
927
1153
|
function toSlug(cat) {
|
|
928
1154
|
return cat.toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-]/g, "");
|
|
929
1155
|
}
|
|
@@ -936,7 +1162,7 @@ function ArticleDetailHero({
|
|
|
936
1162
|
}) {
|
|
937
1163
|
const showConfiguredAuthors = showAuthor && authors.length > 0;
|
|
938
1164
|
const showLegacyAuthor = showAuthor && authors.length === 0 && article.author.trim().length > 0;
|
|
939
|
-
return /* @__PURE__ */ (0,
|
|
1165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
940
1166
|
"section",
|
|
941
1167
|
{
|
|
942
1168
|
style: {
|
|
@@ -948,8 +1174,8 @@ function ArticleDetailHero({
|
|
|
948
1174
|
overflow: "hidden"
|
|
949
1175
|
},
|
|
950
1176
|
children: [
|
|
951
|
-
/* @__PURE__ */ (0,
|
|
952
|
-
|
|
1177
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1178
|
+
import_image5.default,
|
|
953
1179
|
{
|
|
954
1180
|
src: article.featuredImage,
|
|
955
1181
|
alt: article.title,
|
|
@@ -960,7 +1186,7 @@ function ArticleDetailHero({
|
|
|
960
1186
|
priority: true
|
|
961
1187
|
}
|
|
962
1188
|
),
|
|
963
|
-
/* @__PURE__ */ (0,
|
|
1189
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
964
1190
|
"div",
|
|
965
1191
|
{
|
|
966
1192
|
style: {
|
|
@@ -970,7 +1196,7 @@ function ArticleDetailHero({
|
|
|
970
1196
|
}
|
|
971
1197
|
}
|
|
972
1198
|
),
|
|
973
|
-
/* @__PURE__ */ (0,
|
|
1199
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
974
1200
|
"div",
|
|
975
1201
|
{
|
|
976
1202
|
style: {
|
|
@@ -984,7 +1210,7 @@ function ArticleDetailHero({
|
|
|
984
1210
|
width: "100%"
|
|
985
1211
|
},
|
|
986
1212
|
children: [
|
|
987
|
-
article.categories && article.categories.length > 0 && /* @__PURE__ */ (0,
|
|
1213
|
+
article.categories && article.categories.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
988
1214
|
"div",
|
|
989
1215
|
{
|
|
990
1216
|
style: {
|
|
@@ -995,15 +1221,15 @@ function ArticleDetailHero({
|
|
|
995
1221
|
marginBottom: "1rem"
|
|
996
1222
|
},
|
|
997
1223
|
children: article.categories.slice(0, 4).map(
|
|
998
|
-
(cat) => categoryBasePath ? /* @__PURE__ */ (0,
|
|
999
|
-
|
|
1224
|
+
(cat) => categoryBasePath ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1225
|
+
import_link8.default,
|
|
1000
1226
|
{
|
|
1001
1227
|
href: `${categoryBasePath}/${toSlug(cat)}`,
|
|
1002
1228
|
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
1229
|
children: cat
|
|
1004
1230
|
},
|
|
1005
1231
|
cat
|
|
1006
|
-
) : /* @__PURE__ */ (0,
|
|
1232
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1007
1233
|
"span",
|
|
1008
1234
|
{
|
|
1009
1235
|
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full",
|
|
@@ -1014,8 +1240,8 @@ function ArticleDetailHero({
|
|
|
1014
1240
|
)
|
|
1015
1241
|
}
|
|
1016
1242
|
),
|
|
1017
|
-
/* @__PURE__ */ (0,
|
|
1018
|
-
/* @__PURE__ */ (0,
|
|
1243
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-4", children: article.title }),
|
|
1244
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1019
1245
|
"div",
|
|
1020
1246
|
{
|
|
1021
1247
|
style: {
|
|
@@ -1027,23 +1253,23 @@ function ArticleDetailHero({
|
|
|
1027
1253
|
color: "rgba(255,255,255,0.8)"
|
|
1028
1254
|
},
|
|
1029
1255
|
children: [
|
|
1030
|
-
showDate && article.date && /* @__PURE__ */ (0,
|
|
1031
|
-
/* @__PURE__ */ (0,
|
|
1032
|
-
/* @__PURE__ */ (0,
|
|
1256
|
+
showDate && article.date && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Calendar, { className: "h-4 w-4" }),
|
|
1258
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: article.date })
|
|
1033
1259
|
] }),
|
|
1034
|
-
/* @__PURE__ */ (0,
|
|
1035
|
-
/* @__PURE__ */ (0,
|
|
1036
|
-
/* @__PURE__ */ (0,
|
|
1260
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Clock, { className: "h-4 w-4" }),
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: article.readTime })
|
|
1037
1263
|
] }),
|
|
1038
|
-
showConfiguredAuthors && /* @__PURE__ */ (0,
|
|
1039
|
-
/* @__PURE__ */ (0,
|
|
1040
|
-
/* @__PURE__ */ (0,
|
|
1264
|
+
showConfiguredAuthors && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1265
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.User, { className: "h-4 w-4" }),
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
1041
1267
|
"By",
|
|
1042
1268
|
" ",
|
|
1043
|
-
authors.map((author, index) => /* @__PURE__ */ (0,
|
|
1269
|
+
authors.map((author, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react4.Fragment, { children: [
|
|
1044
1270
|
index > 0 && ", ",
|
|
1045
|
-
/* @__PURE__ */ (0,
|
|
1046
|
-
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1272
|
+
import_link8.default,
|
|
1047
1273
|
{
|
|
1048
1274
|
href: `/articles/authors/${author.slug}`,
|
|
1049
1275
|
className: "font-medium text-white underline-offset-4 hover:underline",
|
|
@@ -1053,15 +1279,26 @@ function ArticleDetailHero({
|
|
|
1053
1279
|
] }, author.slug))
|
|
1054
1280
|
] })
|
|
1055
1281
|
] }),
|
|
1056
|
-
showLegacyAuthor && /* @__PURE__ */ (0,
|
|
1057
|
-
/* @__PURE__ */ (0,
|
|
1058
|
-
/* @__PURE__ */ (0,
|
|
1282
|
+
showLegacyAuthor && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
1283
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.User, { className: "h-4 w-4" }),
|
|
1284
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { children: [
|
|
1059
1285
|
"By ",
|
|
1060
1286
|
article.author
|
|
1061
1287
|
] })
|
|
1062
1288
|
] })
|
|
1063
1289
|
]
|
|
1064
1290
|
}
|
|
1291
|
+
),
|
|
1292
|
+
showConfiguredAuthors && authors.length === 1 && authors[0].promise && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1293
|
+
"p",
|
|
1294
|
+
{
|
|
1295
|
+
style: {
|
|
1296
|
+
marginTop: "0.75rem",
|
|
1297
|
+
fontSize: "0.9375rem",
|
|
1298
|
+
color: "rgba(255,255,255,0.85)"
|
|
1299
|
+
},
|
|
1300
|
+
children: authors[0].promise
|
|
1301
|
+
}
|
|
1065
1302
|
)
|
|
1066
1303
|
]
|
|
1067
1304
|
}
|
|
@@ -1072,9 +1309,9 @@ function ArticleDetailHero({
|
|
|
1072
1309
|
}
|
|
1073
1310
|
|
|
1074
1311
|
// src/CategoryArticlesPage.tsx
|
|
1075
|
-
var
|
|
1076
|
-
var
|
|
1077
|
-
var
|
|
1312
|
+
var import_image6 = __toESM(require("next/image"), 1);
|
|
1313
|
+
var import_link9 = __toESM(require("next/link"), 1);
|
|
1314
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1078
1315
|
function getCategoryDescription(config, slug, categoryName) {
|
|
1079
1316
|
var _a;
|
|
1080
1317
|
const raw = (_a = config.categoryDescriptions) == null ? void 0 : _a[slug];
|
|
@@ -1116,7 +1353,14 @@ function buildCategoryBreadcrumbEntry(entry, context) {
|
|
|
1116
1353
|
}
|
|
1117
1354
|
return [{ name: context.categoryName }];
|
|
1118
1355
|
}
|
|
1119
|
-
function CategoryArticlesPage({
|
|
1356
|
+
function CategoryArticlesPage({
|
|
1357
|
+
category,
|
|
1358
|
+
articles,
|
|
1359
|
+
config,
|
|
1360
|
+
page,
|
|
1361
|
+
totalPages,
|
|
1362
|
+
totalCount
|
|
1363
|
+
}) {
|
|
1120
1364
|
var _a;
|
|
1121
1365
|
if (articles.length === 0) return null;
|
|
1122
1366
|
const categoryName = articles[0].category;
|
|
@@ -1125,8 +1369,25 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1125
1369
|
const pageSize = (_a = config.pageSize) != null ? _a : DEFAULT_PAGE_SIZE;
|
|
1126
1370
|
const breadcrumbConfig = getBreadcrumbsConfig(config);
|
|
1127
1371
|
const breadcrumbItems = buildCategoryBreadcrumbItems(config, categoryName);
|
|
1128
|
-
|
|
1129
|
-
|
|
1372
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
1373
|
+
const articleCount = totalCount != null ? totalCount : articles.length;
|
|
1374
|
+
const pagination = config.listingPagination === "pages" && page !== void 0 && totalPages !== void 0 ? { page, totalPages, basePath: `/articles/category/${category}` } : void 0;
|
|
1375
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
1376
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1377
|
+
CollectionPageSchema,
|
|
1378
|
+
{
|
|
1379
|
+
title: `${categoryName} | ${config.siteName}`,
|
|
1380
|
+
description: description.short,
|
|
1381
|
+
url: `${siteUrl}/articles/category/${category}`,
|
|
1382
|
+
articleCount,
|
|
1383
|
+
items: articles.slice(0, pageSize).map((article, index) => ({
|
|
1384
|
+
position: index + 1,
|
|
1385
|
+
url: `${siteUrl}/articles/${article.slug}`,
|
|
1386
|
+
name: article.title
|
|
1387
|
+
}))
|
|
1388
|
+
}
|
|
1389
|
+
),
|
|
1390
|
+
breadcrumbsAreEnabled(config) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1130
1391
|
Breadcrumb,
|
|
1131
1392
|
{
|
|
1132
1393
|
items: breadcrumbItems,
|
|
@@ -1134,7 +1395,7 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1134
1395
|
showSchema: breadcrumbConfig.showSchema !== false
|
|
1135
1396
|
}
|
|
1136
1397
|
),
|
|
1137
|
-
/* @__PURE__ */ (0,
|
|
1398
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
1138
1399
|
"section",
|
|
1139
1400
|
{
|
|
1140
1401
|
style: {
|
|
@@ -1146,8 +1407,8 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1146
1407
|
overflow: "hidden"
|
|
1147
1408
|
},
|
|
1148
1409
|
children: [
|
|
1149
|
-
/* @__PURE__ */ (0,
|
|
1150
|
-
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1411
|
+
import_image6.default,
|
|
1151
1412
|
{
|
|
1152
1413
|
src: heroImage,
|
|
1153
1414
|
alt: categoryName,
|
|
@@ -1157,8 +1418,8 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1157
1418
|
priority: true
|
|
1158
1419
|
}
|
|
1159
1420
|
),
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1161
|
-
/* @__PURE__ */ (0,
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: { position: "absolute", inset: 0, backgroundColor: "rgba(0,0,0,0.6)" } }),
|
|
1422
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
1162
1423
|
"div",
|
|
1163
1424
|
{
|
|
1164
1425
|
style: {
|
|
@@ -1169,10 +1430,10 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1169
1430
|
padding: "0 1rem"
|
|
1170
1431
|
},
|
|
1171
1432
|
children: [
|
|
1172
|
-
/* @__PURE__ */ (0,
|
|
1173
|
-
/* @__PURE__ */ (0,
|
|
1174
|
-
description.long && /* @__PURE__ */ (0,
|
|
1175
|
-
/* @__PURE__ */ (0,
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-semibold uppercase tracking-widest mb-3 opacity-80", children: "Category" }),
|
|
1434
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-3", children: categoryName }),
|
|
1435
|
+
description.long && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-lg opacity-90 max-w-2xl mx-auto mt-2", children: description.long }),
|
|
1436
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "text-lg opacity-70 mt-2", children: [
|
|
1176
1437
|
articles.length,
|
|
1177
1438
|
" article",
|
|
1178
1439
|
articles.length === 1 ? "" : "s"
|
|
@@ -1183,140 +1444,75 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
1183
1444
|
]
|
|
1184
1445
|
}
|
|
1185
1446
|
),
|
|
1186
|
-
/* @__PURE__ */ (0,
|
|
1187
|
-
/* @__PURE__ */ (0,
|
|
1188
|
-
/* @__PURE__ */ (0,
|
|
1189
|
-
/* @__PURE__ */ (0,
|
|
1447
|
+
/* @__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: [
|
|
1448
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_link9.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
|
|
1450
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground", children: description.short })
|
|
1190
1451
|
] }),
|
|
1191
|
-
/* @__PURE__ */ (0,
|
|
1452
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(LatestArticles, { articles, pageSize, pagination })
|
|
1192
1453
|
] }) })
|
|
1193
1454
|
] });
|
|
1194
1455
|
}
|
|
1195
1456
|
|
|
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 }) {
|
|
1457
|
+
// src/SeriesArticlesPage.tsx
|
|
1458
|
+
var import_link10 = __toESM(require("next/link"), 1);
|
|
1459
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1460
|
+
function SeriesArticlesPage({ seriesSlug, articles, config }) {
|
|
1218
1461
|
var _a;
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1462
|
+
if (articles.length === 0) return null;
|
|
1463
|
+
const seriesName = (_a = articles[0].series) != null ? _a : seriesSlug;
|
|
1464
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
1465
|
+
const seriesUrl = `${siteUrl}/articles/series/${seriesSlug}`;
|
|
1466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
1467
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1468
|
+
CollectionPageSchema,
|
|
1223
1469
|
{
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1470
|
+
title: `${seriesName} | ${config.siteName}`,
|
|
1471
|
+
description: `Follow the ${seriesName} series on ${config.siteName}.`,
|
|
1472
|
+
url: seriesUrl,
|
|
1473
|
+
articleCount: articles.length,
|
|
1474
|
+
items: articles.map((article, index) => ({
|
|
1475
|
+
position: index + 1,
|
|
1476
|
+
url: `${siteUrl}/articles/${article.slug}`,
|
|
1477
|
+
name: article.title
|
|
1478
|
+
}))
|
|
1228
1479
|
}
|
|
1229
1480
|
),
|
|
1230
|
-
/* @__PURE__ */ (0,
|
|
1231
|
-
/* @__PURE__ */ (0,
|
|
1232
|
-
/* @__PURE__ */ (0,
|
|
1233
|
-
/* @__PURE__ */ (0,
|
|
1234
|
-
|
|
1235
|
-
|
|
1481
|
+
/* @__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: [
|
|
1482
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
|
|
1483
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_link10.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
|
|
1484
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1485
|
+
articles.length,
|
|
1486
|
+
" article",
|
|
1487
|
+
articles.length === 1 ? "" : "s",
|
|
1488
|
+
" in this series"
|
|
1236
1489
|
] })
|
|
1237
1490
|
] }),
|
|
1238
|
-
/* @__PURE__ */ (0,
|
|
1491
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h1", { className: "text-3xl font-bold text-foreground mb-8", children: seriesName }),
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1493
|
+
LatestArticles,
|
|
1494
|
+
{
|
|
1495
|
+
articles,
|
|
1496
|
+
pageSize: Math.max(articles.length, DEFAULT_PAGE_SIZE)
|
|
1497
|
+
}
|
|
1498
|
+
)
|
|
1239
1499
|
] }) })
|
|
1240
1500
|
] });
|
|
1241
1501
|
}
|
|
1242
1502
|
|
|
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
|
-
}
|
|
1503
|
+
// src/AuthorArticlesPage.tsx
|
|
1504
|
+
var import_link11 = __toESM(require("next/link"), 1);
|
|
1309
1505
|
|
|
1310
1506
|
// src/AuthorDetailHero.tsx
|
|
1311
1507
|
var import_image7 = __toESM(require("next/image"), 1);
|
|
1312
|
-
var
|
|
1508
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1313
1509
|
function getInitials2(name) {
|
|
1314
1510
|
return name.split(" ").filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join("");
|
|
1315
1511
|
}
|
|
1316
1512
|
function AuthorDetailHero({ author, articleCount }) {
|
|
1317
1513
|
const avatar = getAuthorAvatar(author);
|
|
1318
|
-
return /* @__PURE__ */ (0,
|
|
1319
|
-
avatar ? /* @__PURE__ */ (0,
|
|
1514
|
+
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: [
|
|
1515
|
+
avatar ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1320
1516
|
import_image7.default,
|
|
1321
1517
|
{
|
|
1322
1518
|
src: avatar,
|
|
@@ -1326,27 +1522,303 @@ function AuthorDetailHero({ author, articleCount }) {
|
|
|
1326
1522
|
className: "h-28 w-28 rounded-full object-cover",
|
|
1327
1523
|
priority: true
|
|
1328
1524
|
}
|
|
1329
|
-
) : /* @__PURE__ */ (0,
|
|
1330
|
-
/* @__PURE__ */ (0,
|
|
1331
|
-
/* @__PURE__ */ (0,
|
|
1332
|
-
/* @__PURE__ */ (0,
|
|
1333
|
-
/* @__PURE__ */ (0,
|
|
1334
|
-
typeof articleCount === "number" && /* @__PURE__ */ (0,
|
|
1525
|
+
) : /* @__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) }),
|
|
1526
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-widest text-muted-foreground", children: "Author" }),
|
|
1528
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h1", { className: "text-4xl font-bold text-foreground md:text-5xl", children: author.name }),
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mx-auto mt-4 max-w-2xl text-lg text-muted-foreground", children: author.bio }),
|
|
1530
|
+
typeof articleCount === "number" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "mt-3 text-sm text-muted-foreground", children: [
|
|
1335
1531
|
articleCount,
|
|
1336
1532
|
" article",
|
|
1337
1533
|
articleCount === 1 ? "" : "s"
|
|
1338
1534
|
] })
|
|
1339
1535
|
] }),
|
|
1340
|
-
/* @__PURE__ */ (0,
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AuthorSocialLinks, { author })
|
|
1341
1537
|
] }) });
|
|
1342
1538
|
}
|
|
1343
1539
|
|
|
1344
|
-
// src/
|
|
1540
|
+
// src/eventTracking.tsx
|
|
1345
1541
|
var import_react5 = require("react");
|
|
1346
|
-
var
|
|
1347
|
-
var
|
|
1348
|
-
|
|
1349
|
-
|
|
1542
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1543
|
+
var AVERAGE_WORDS_PER_MINUTE = 200;
|
|
1544
|
+
var DEFAULT_MEANINGFUL_READ_MS = 15e3;
|
|
1545
|
+
function ArticleViewTracker({ article, config }) {
|
|
1546
|
+
const firedMeaningfulRead = (0, import_react5.useRef)(false);
|
|
1547
|
+
(0, import_react5.useEffect)(() => {
|
|
1548
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
1549
|
+
name: "article_viewed",
|
|
1550
|
+
articleSlug: article.slug,
|
|
1551
|
+
category: article.category,
|
|
1552
|
+
seriesSlug: article.seriesSlug
|
|
1553
|
+
});
|
|
1554
|
+
const estimatedMs = article.wordCount ? article.wordCount / AVERAGE_WORDS_PER_MINUTE * 6e4 * 0.5 : DEFAULT_MEANINGFUL_READ_MS;
|
|
1555
|
+
const timer = setTimeout(() => {
|
|
1556
|
+
if (firedMeaningfulRead.current) return;
|
|
1557
|
+
firedMeaningfulRead.current = true;
|
|
1558
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, { name: "meaningful_read", articleSlug: article.slug });
|
|
1559
|
+
}, estimatedMs);
|
|
1560
|
+
return () => clearTimeout(timer);
|
|
1561
|
+
}, [article.slug]);
|
|
1562
|
+
return null;
|
|
1563
|
+
}
|
|
1564
|
+
function CtaViewTracker({ ctaId, articleSlug, config, children }) {
|
|
1565
|
+
const ref = (0, import_react5.useRef)(null);
|
|
1566
|
+
const fired = (0, import_react5.useRef)(false);
|
|
1567
|
+
(0, import_react5.useEffect)(() => {
|
|
1568
|
+
const node = ref.current;
|
|
1569
|
+
if (!node) return;
|
|
1570
|
+
function fire() {
|
|
1571
|
+
if (fired.current) return;
|
|
1572
|
+
fired.current = true;
|
|
1573
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, { name: "cta_viewed", ctaId, articleSlug });
|
|
1574
|
+
}
|
|
1575
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
1576
|
+
fire();
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
const observer = new IntersectionObserver(
|
|
1580
|
+
(entries) => {
|
|
1581
|
+
if (entries.some((entry) => entry.isIntersecting)) fire();
|
|
1582
|
+
},
|
|
1583
|
+
{ threshold: 0.5 }
|
|
1584
|
+
);
|
|
1585
|
+
observer.observe(node);
|
|
1586
|
+
return () => observer.disconnect();
|
|
1587
|
+
}, [ctaId, articleSlug]);
|
|
1588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { ref, children });
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
// src/AuthorArticlesPage.tsx
|
|
1592
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1593
|
+
function getPersonSchema(author, config, articleCount) {
|
|
1594
|
+
var _a, _b;
|
|
1595
|
+
return __spreadProps(__spreadValues(__spreadValues({
|
|
1596
|
+
"@context": "https://schema.org",
|
|
1597
|
+
"@type": "Person",
|
|
1598
|
+
name: author.name,
|
|
1599
|
+
description: author.bio,
|
|
1600
|
+
url: (_a = author.url) != null ? _a : getAuthorUrl(author)
|
|
1601
|
+
}, getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }), {
|
|
1602
|
+
knowsAbout: config.siteName,
|
|
1603
|
+
mainEntityOfPage: (_b = author.url) != null ? _b : `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`,
|
|
1604
|
+
interactionStatistic: {
|
|
1605
|
+
"@type": "InteractionCounter",
|
|
1606
|
+
interactionType: "https://schema.org/WriteAction",
|
|
1607
|
+
userInteractionCount: articleCount
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
function renderHeroSection(author, articleCount) {
|
|
1612
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AuthorDetailHero, { author, articleCount }, "hero");
|
|
1613
|
+
}
|
|
1614
|
+
function renderPromiseSection(author) {
|
|
1615
|
+
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;
|
|
1616
|
+
}
|
|
1617
|
+
function renderServesWhoSection(author) {
|
|
1618
|
+
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: [
|
|
1619
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "mb-4 text-center text-2xl font-bold text-foreground", children: "Who I help" }),
|
|
1620
|
+
/* @__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)(
|
|
1621
|
+
"li",
|
|
1622
|
+
{
|
|
1623
|
+
className: "rounded-lg border border-border bg-card px-4 py-3 text-sm text-card-foreground",
|
|
1624
|
+
children: who
|
|
1625
|
+
},
|
|
1626
|
+
who
|
|
1627
|
+
)) })
|
|
1628
|
+
] }) }, "servesWho") : null;
|
|
1629
|
+
}
|
|
1630
|
+
function renderOriginStorySection(author) {
|
|
1631
|
+
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: [
|
|
1632
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "mb-6 text-2xl font-bold text-foreground", children: "My story" }),
|
|
1633
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "space-y-6", children: author.originStory.map((block, blockIndex) => {
|
|
1634
|
+
var _a;
|
|
1635
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
1636
|
+
block.heading && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h3", { className: "mb-2 text-lg font-semibold text-foreground", children: block.heading }),
|
|
1637
|
+
block.paragraphs.map((paragraph, paragraphIndex) => {
|
|
1638
|
+
var _a2;
|
|
1639
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1640
|
+
"p",
|
|
1641
|
+
{
|
|
1642
|
+
className: "mb-3 text-muted-foreground",
|
|
1643
|
+
children: paragraph
|
|
1644
|
+
},
|
|
1645
|
+
`${(_a2 = block.heading) != null ? _a2 : blockIndex}-${paragraph.slice(0, 40)}-${paragraphIndex}`
|
|
1646
|
+
);
|
|
1647
|
+
})
|
|
1648
|
+
] }, (_a = block.heading) != null ? _a : blockIndex);
|
|
1649
|
+
}) })
|
|
1650
|
+
] }) }, "originStory") : null;
|
|
1651
|
+
}
|
|
1652
|
+
function renderPrinciplesSection(author) {
|
|
1653
|
+
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: [
|
|
1654
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "mb-4 text-center text-2xl font-bold text-foreground", children: "What I believe" }),
|
|
1655
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "space-y-3", children: author.principles.map((principle) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1656
|
+
"li",
|
|
1657
|
+
{
|
|
1658
|
+
className: "rounded-lg border border-border bg-card px-4 py-3 text-sm text-card-foreground",
|
|
1659
|
+
children: principle
|
|
1660
|
+
},
|
|
1661
|
+
principle
|
|
1662
|
+
)) })
|
|
1663
|
+
] }) }, "principles") : null;
|
|
1664
|
+
}
|
|
1665
|
+
function renderProofSection(author) {
|
|
1666
|
+
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: [
|
|
1667
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { className: "mb-6 text-center text-2xl font-bold text-foreground", children: "Proof" }),
|
|
1668
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "grid gap-4 sm:grid-cols-2", children: author.proof.map((item) => {
|
|
1669
|
+
var _a, _b, _c;
|
|
1670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1671
|
+
"div",
|
|
1672
|
+
{
|
|
1673
|
+
className: "rounded-lg border border-border bg-card p-4 text-card-foreground",
|
|
1674
|
+
children: [
|
|
1675
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "font-medium", children: item.claim }),
|
|
1676
|
+
(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 })
|
|
1677
|
+
]
|
|
1678
|
+
},
|
|
1679
|
+
`${item.claim}-${(_b = (_a = item.url) != null ? _a : item.source) != null ? _b : ""}`
|
|
1680
|
+
);
|
|
1681
|
+
}) })
|
|
1682
|
+
] }) }, "proof") : null;
|
|
1683
|
+
}
|
|
1684
|
+
function renderCtaSection(author, config) {
|
|
1685
|
+
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)(
|
|
1686
|
+
import_link11.default,
|
|
1687
|
+
{
|
|
1688
|
+
href: author.primaryCta.href,
|
|
1689
|
+
onClick: () => emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
1690
|
+
name: "cta_clicked",
|
|
1691
|
+
ctaId: `author-cta:${author.slug}`
|
|
1692
|
+
}),
|
|
1693
|
+
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",
|
|
1694
|
+
children: author.primaryCta.label
|
|
1695
|
+
}
|
|
1696
|
+
) }) }) }, "cta") : null;
|
|
1697
|
+
}
|
|
1698
|
+
function renderArticlesSection(author, articles, pageSize, pagination) {
|
|
1699
|
+
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: [
|
|
1700
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mb-8 flex items-center justify-between gap-4", children: [
|
|
1701
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_link11.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "All Articles" }),
|
|
1702
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1703
|
+
"Articles by ",
|
|
1704
|
+
author.name
|
|
1705
|
+
] })
|
|
1706
|
+
] }),
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LatestArticles, { articles, pageSize, pagination })
|
|
1708
|
+
] }) }, "articles");
|
|
1709
|
+
}
|
|
1710
|
+
function renderAuthorSection(section, ctx) {
|
|
1711
|
+
const { author, articleCount, articles, pageSize, pagination, customSection, config } = ctx;
|
|
1712
|
+
switch (section) {
|
|
1713
|
+
case "hero":
|
|
1714
|
+
return renderHeroSection(author, articleCount);
|
|
1715
|
+
case "promise":
|
|
1716
|
+
return renderPromiseSection(author);
|
|
1717
|
+
case "servesWho":
|
|
1718
|
+
return renderServesWhoSection(author);
|
|
1719
|
+
case "originStory":
|
|
1720
|
+
return renderOriginStorySection(author);
|
|
1721
|
+
case "principles":
|
|
1722
|
+
return renderPrinciplesSection(author);
|
|
1723
|
+
case "proof":
|
|
1724
|
+
return renderProofSection(author);
|
|
1725
|
+
case "cta":
|
|
1726
|
+
return renderCtaSection(author, config);
|
|
1727
|
+
case "articles":
|
|
1728
|
+
return renderArticlesSection(author, articles, pageSize, pagination);
|
|
1729
|
+
case "custom":
|
|
1730
|
+
return customSection != null ? customSection : null;
|
|
1731
|
+
default:
|
|
1732
|
+
return null;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
function AuthorArticlesPage({
|
|
1736
|
+
author,
|
|
1737
|
+
articles,
|
|
1738
|
+
config,
|
|
1739
|
+
page,
|
|
1740
|
+
totalPages,
|
|
1741
|
+
totalCount,
|
|
1742
|
+
sections,
|
|
1743
|
+
customSection
|
|
1744
|
+
}) {
|
|
1745
|
+
var _a, _b;
|
|
1746
|
+
const pageSize = (_a = config.pageSize) != null ? _a : DEFAULT_PAGE_SIZE;
|
|
1747
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
1748
|
+
const articleCount = totalCount != null ? totalCount : articles.length;
|
|
1749
|
+
const pagination = config.listingPagination === "pages" && page !== void 0 && totalPages !== void 0 ? { page, totalPages, basePath: `/articles/authors/${author.slug}` } : void 0;
|
|
1750
|
+
const schemas = /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
|
|
1751
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1752
|
+
"script",
|
|
1753
|
+
{
|
|
1754
|
+
type: "application/ld+json",
|
|
1755
|
+
dangerouslySetInnerHTML: {
|
|
1756
|
+
__html: JSON.stringify(getPersonSchema(author, config, articleCount))
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
),
|
|
1760
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1761
|
+
CollectionPageSchema,
|
|
1762
|
+
{
|
|
1763
|
+
title: `${author.name} | ${config.siteName}`,
|
|
1764
|
+
description: `Articles by ${author.name} on ${config.siteName}.`,
|
|
1765
|
+
url: (_b = author.url) != null ? _b : `${siteUrl}/articles/authors/${author.slug}`,
|
|
1766
|
+
articleCount,
|
|
1767
|
+
items: articles.slice(0, pageSize).map((article, index) => ({
|
|
1768
|
+
position: index + 1,
|
|
1769
|
+
url: `${siteUrl}/articles/${article.slug}`,
|
|
1770
|
+
name: article.title
|
|
1771
|
+
}))
|
|
1772
|
+
}
|
|
1773
|
+
)
|
|
1774
|
+
] });
|
|
1775
|
+
if (!sections) {
|
|
1776
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
1777
|
+
schemas,
|
|
1778
|
+
/* @__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: [
|
|
1779
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "mb-8 flex items-center justify-between gap-4", children: [
|
|
1780
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_link11.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "All Articles" }),
|
|
1781
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1782
|
+
"Articles by ",
|
|
1783
|
+
author.name
|
|
1784
|
+
] })
|
|
1785
|
+
] }),
|
|
1786
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LatestArticles, { articles, pageSize, pagination })
|
|
1787
|
+
] }) })
|
|
1788
|
+
] });
|
|
1789
|
+
}
|
|
1790
|
+
const sectionCtx = {
|
|
1791
|
+
author,
|
|
1792
|
+
articleCount,
|
|
1793
|
+
articles,
|
|
1794
|
+
pageSize,
|
|
1795
|
+
pagination,
|
|
1796
|
+
customSection,
|
|
1797
|
+
config
|
|
1798
|
+
};
|
|
1799
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
1800
|
+
schemas,
|
|
1801
|
+
sections.map((section) => renderAuthorSection(section, sectionCtx))
|
|
1802
|
+
] });
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// src/ArticleSocialShare.tsx
|
|
1806
|
+
var import_react6 = require("react");
|
|
1807
|
+
var import_lucide_react8 = require("lucide-react");
|
|
1808
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1809
|
+
function ArticleSocialShare({
|
|
1810
|
+
title,
|
|
1811
|
+
url,
|
|
1812
|
+
excerpt,
|
|
1813
|
+
shareMessage,
|
|
1814
|
+
config,
|
|
1815
|
+
articleSlug
|
|
1816
|
+
}) {
|
|
1817
|
+
const [copied, setCopied] = (0, import_react6.useState)(false);
|
|
1818
|
+
function trackShare(channel) {
|
|
1819
|
+
if (!articleSlug) return;
|
|
1820
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, { name: "shared", articleSlug, channel });
|
|
1821
|
+
}
|
|
1350
1822
|
const encodedTitle = encodeURIComponent(title);
|
|
1351
1823
|
const encodedUrl = encodeURIComponent(url);
|
|
1352
1824
|
const encodedExcerpt = encodeURIComponent(excerpt || "");
|
|
@@ -1363,102 +1835,203 @@ function ArticleSocialShare({ title, url, excerpt, shareMessage }) {
|
|
|
1363
1835
|
try {
|
|
1364
1836
|
yield navigator.clipboard.writeText(url);
|
|
1365
1837
|
setCopied(true);
|
|
1838
|
+
trackShare("copy-link");
|
|
1366
1839
|
setTimeout(() => setCopied(false), 2e3);
|
|
1367
1840
|
} catch (e) {
|
|
1368
1841
|
}
|
|
1369
1842
|
});
|
|
1370
|
-
const openShareWindow = (shareUrl) => {
|
|
1843
|
+
const openShareWindow = (shareUrl, channel) => {
|
|
1844
|
+
trackShare(channel);
|
|
1371
1845
|
globalThis.open(shareUrl, "_blank", "width=600,height=400,scrollbars=yes,resizable=yes");
|
|
1372
1846
|
};
|
|
1373
1847
|
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,
|
|
1848
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "border-t border-border pt-8 mt-8", children: [
|
|
1849
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2 mb-4", children: [
|
|
1850
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Share2, { className: "h-5 w-5 text-muted-foreground" }),
|
|
1851
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { className: "text-lg font-semibold text-foreground", children: "Share this article" })
|
|
1378
1852
|
] }),
|
|
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
|
-
|
|
1853
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-wrap gap-2", children: [
|
|
1854
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1855
|
+
"button",
|
|
1856
|
+
{
|
|
1857
|
+
type: "button",
|
|
1858
|
+
className: btnClass,
|
|
1859
|
+
onClick: () => openShareWindow(shareLinks.linkedin, "linkedin"),
|
|
1860
|
+
children: [
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Users, { className: "h-4 w-4" }),
|
|
1862
|
+
"LinkedIn"
|
|
1863
|
+
]
|
|
1864
|
+
}
|
|
1865
|
+
),
|
|
1866
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1867
|
+
"button",
|
|
1868
|
+
{
|
|
1869
|
+
type: "button",
|
|
1870
|
+
className: btnClass,
|
|
1871
|
+
onClick: () => openShareWindow(shareLinks.facebook, "facebook"),
|
|
1872
|
+
children: [
|
|
1873
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-xs font-bold", children: "f" }),
|
|
1874
|
+
" Facebook"
|
|
1875
|
+
]
|
|
1876
|
+
}
|
|
1877
|
+
),
|
|
1878
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1879
|
+
"button",
|
|
1880
|
+
{
|
|
1881
|
+
type: "button",
|
|
1882
|
+
className: btnClass,
|
|
1883
|
+
onClick: () => openShareWindow(shareLinks.twitter, "twitter"),
|
|
1884
|
+
children: [
|
|
1885
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.MessageCircle, { className: "h-4 w-4" }),
|
|
1886
|
+
"Twitter"
|
|
1887
|
+
]
|
|
1888
|
+
}
|
|
1889
|
+
),
|
|
1890
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1891
|
+
"button",
|
|
1892
|
+
{
|
|
1893
|
+
type: "button",
|
|
1894
|
+
className: btnClass,
|
|
1895
|
+
onClick: () => openShareWindow(shareLinks.reddit, "reddit"),
|
|
1896
|
+
children: [
|
|
1897
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.FileText, { className: "h-4 w-4" }),
|
|
1898
|
+
"Reddit"
|
|
1899
|
+
]
|
|
1900
|
+
}
|
|
1901
|
+
),
|
|
1902
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1903
|
+
"button",
|
|
1904
|
+
{
|
|
1905
|
+
type: "button",
|
|
1906
|
+
className: btnClass,
|
|
1907
|
+
onClick: () => openShareWindow(shareLinks.whatsapp, "whatsapp"),
|
|
1908
|
+
children: [
|
|
1909
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.MessageCircle, { className: "h-4 w-4" }),
|
|
1910
|
+
"WhatsApp"
|
|
1911
|
+
]
|
|
1912
|
+
}
|
|
1913
|
+
),
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1915
|
+
"button",
|
|
1916
|
+
{
|
|
1917
|
+
type: "button",
|
|
1918
|
+
className: btnClass,
|
|
1919
|
+
onClick: () => openShareWindow(shareLinks.telegram, "telegram"),
|
|
1920
|
+
children: [
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.MessageCircle, { className: "h-4 w-4" }),
|
|
1922
|
+
"Telegram"
|
|
1923
|
+
]
|
|
1924
|
+
}
|
|
1925
|
+
),
|
|
1926
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("a", { className: btnClass, href: shareLinks.email, onClick: () => trackShare("email"), children: [
|
|
1927
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Mail, { className: "h-4 w-4" }),
|
|
1406
1928
|
"Email"
|
|
1407
1929
|
] }),
|
|
1408
|
-
/* @__PURE__ */ (0,
|
|
1409
|
-
copied ? /* @__PURE__ */ (0,
|
|
1930
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("button", { type: "button", className: btnClass, onClick: copyToClipboard, children: [
|
|
1931
|
+
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
1932
|
copied ? "Copied!" : "Copy Link"
|
|
1411
1933
|
] })
|
|
1412
1934
|
] }),
|
|
1413
|
-
shareMessage && /* @__PURE__ */ (0,
|
|
1935
|
+
shareMessage && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-sm text-muted-foreground mt-3", children: shareMessage })
|
|
1414
1936
|
] });
|
|
1415
1937
|
}
|
|
1416
1938
|
|
|
1417
1939
|
// src/ArticleNavigation.tsx
|
|
1418
|
-
var
|
|
1419
|
-
var
|
|
1420
|
-
var
|
|
1940
|
+
var import_link12 = __toESM(require("next/link"), 1);
|
|
1941
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1942
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1421
1943
|
function truncateTitle(title, maxLength = 60) {
|
|
1422
1944
|
return title.length > maxLength ? `${title.substring(0, maxLength)}...` : title;
|
|
1423
1945
|
}
|
|
1424
|
-
function ArticleNavigation({
|
|
1946
|
+
function ArticleNavigation({
|
|
1947
|
+
previous,
|
|
1948
|
+
next,
|
|
1949
|
+
basePath,
|
|
1950
|
+
fromSlug,
|
|
1951
|
+
pathKey,
|
|
1952
|
+
config
|
|
1953
|
+
}) {
|
|
1425
1954
|
if (!previous && !next) return null;
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1955
|
+
function emitStep(toSlug2, direction) {
|
|
1956
|
+
if (!pathKey || !fromSlug) return;
|
|
1957
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
1958
|
+
name: "path_step_advanced",
|
|
1959
|
+
pathKey,
|
|
1960
|
+
fromSlug,
|
|
1961
|
+
toSlug: toSlug2,
|
|
1962
|
+
direction
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
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: [
|
|
1966
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1 min-w-0", children: previous && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1967
|
+
import_link12.default,
|
|
1429
1968
|
{
|
|
1430
1969
|
href: `${basePath}/${previous.slug}`,
|
|
1970
|
+
onClick: () => emitStep(previous.slug, "previous"),
|
|
1431
1971
|
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
1972
|
children: [
|
|
1433
|
-
/* @__PURE__ */ (0,
|
|
1434
|
-
/* @__PURE__ */ (0,
|
|
1435
|
-
/* @__PURE__ */ (0,
|
|
1436
|
-
/* @__PURE__ */ (0,
|
|
1973
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronLeft, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" }),
|
|
1974
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1975
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-sm text-muted-foreground mb-1", children: "Previous Article" }),
|
|
1976
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "font-medium text-foreground truncate", title: previous.title, children: truncateTitle(previous.title) })
|
|
1437
1977
|
] })
|
|
1438
1978
|
]
|
|
1439
1979
|
}
|
|
1440
1980
|
) }),
|
|
1441
|
-
/* @__PURE__ */ (0,
|
|
1442
|
-
|
|
1981
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1 min-w-0", children: next && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1982
|
+
import_link12.default,
|
|
1443
1983
|
{
|
|
1444
1984
|
href: `${basePath}/${next.slug}`,
|
|
1985
|
+
onClick: () => emitStep(next.slug, "next"),
|
|
1445
1986
|
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
1987
|
children: [
|
|
1447
|
-
/* @__PURE__ */ (0,
|
|
1448
|
-
/* @__PURE__ */ (0,
|
|
1449
|
-
/* @__PURE__ */ (0,
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1 text-right", children: [
|
|
1989
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "text-sm text-muted-foreground mb-1", children: "Next Article" }),
|
|
1990
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "font-medium text-foreground truncate", title: next.title, children: truncateTitle(next.title) })
|
|
1450
1991
|
] }),
|
|
1451
|
-
/* @__PURE__ */ (0,
|
|
1992
|
+
/* @__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
1993
|
]
|
|
1453
1994
|
}
|
|
1454
1995
|
) })
|
|
1455
1996
|
] }) });
|
|
1456
1997
|
}
|
|
1457
1998
|
|
|
1999
|
+
// src/RelatedArticlesSection.tsx
|
|
2000
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
2001
|
+
function RelatedArticlesSection({
|
|
2002
|
+
articles,
|
|
2003
|
+
category,
|
|
2004
|
+
heading,
|
|
2005
|
+
config,
|
|
2006
|
+
fromSlug,
|
|
2007
|
+
source = "category"
|
|
2008
|
+
}) {
|
|
2009
|
+
if (articles.length === 0) return null;
|
|
2010
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("section", { className: "mt-12 pt-8 border-t border-border", "aria-label": "Related articles", children: [
|
|
2011
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h2", { className: "text-xl font-semibold text-foreground mb-6", children: heading != null ? heading : `More in ${category}` }),
|
|
2012
|
+
/* @__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)(
|
|
2013
|
+
"div",
|
|
2014
|
+
{
|
|
2015
|
+
onClickCapture: () => {
|
|
2016
|
+
if (!fromSlug) return;
|
|
2017
|
+
emitArticleEvent(config == null ? void 0 : config.onEvent, {
|
|
2018
|
+
name: "related_article_clicked",
|
|
2019
|
+
fromSlug,
|
|
2020
|
+
toSlug: article.slug,
|
|
2021
|
+
source
|
|
2022
|
+
});
|
|
2023
|
+
},
|
|
2024
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ArticleCard, { article, config })
|
|
2025
|
+
},
|
|
2026
|
+
article.slug
|
|
2027
|
+
)) })
|
|
2028
|
+
] });
|
|
2029
|
+
}
|
|
2030
|
+
|
|
1458
2031
|
// src/ArticleBackLink.tsx
|
|
1459
|
-
var
|
|
1460
|
-
var
|
|
1461
|
-
var
|
|
2032
|
+
var import_link13 = __toESM(require("next/link"), 1);
|
|
2033
|
+
var import_lucide_react10 = require("lucide-react");
|
|
2034
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1462
2035
|
function ArticleBackLink({
|
|
1463
2036
|
config,
|
|
1464
2037
|
href = "/articles",
|
|
@@ -1466,13 +2039,13 @@ function ArticleBackLink({
|
|
|
1466
2039
|
className
|
|
1467
2040
|
}) {
|
|
1468
2041
|
if (config.showBackToArticles === false) return null;
|
|
1469
|
-
return /* @__PURE__ */ (0,
|
|
1470
|
-
|
|
2042
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2043
|
+
import_link13.default,
|
|
1471
2044
|
{
|
|
1472
2045
|
href,
|
|
1473
2046
|
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
2047
|
children: [
|
|
1475
|
-
/* @__PURE__ */ (0,
|
|
2048
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ArrowLeft, { className: "h-4 w-4" }),
|
|
1476
2049
|
label
|
|
1477
2050
|
]
|
|
1478
2051
|
}
|
|
@@ -1480,17 +2053,17 @@ function ArticleBackLink({
|
|
|
1480
2053
|
}
|
|
1481
2054
|
|
|
1482
2055
|
// src/ArticleTOC.tsx
|
|
1483
|
-
var
|
|
2056
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1484
2057
|
function ArticleTOC({ toc, className }) {
|
|
1485
2058
|
if (!toc.length) return null;
|
|
1486
|
-
return /* @__PURE__ */ (0,
|
|
2059
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1487
2060
|
"nav",
|
|
1488
2061
|
{
|
|
1489
2062
|
"aria-label": "Table of contents",
|
|
1490
2063
|
className: `mb-8 rounded-lg border border-border bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
|
|
1491
2064
|
children: [
|
|
1492
|
-
/* @__PURE__ */ (0,
|
|
1493
|
-
/* @__PURE__ */ (0,
|
|
2065
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "On this page" }),
|
|
2066
|
+
/* @__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
2067
|
"a",
|
|
1495
2068
|
{
|
|
1496
2069
|
href: `#${item.id}`,
|
|
@@ -1504,35 +2077,35 @@ function ArticleTOC({ toc, className }) {
|
|
|
1504
2077
|
}
|
|
1505
2078
|
|
|
1506
2079
|
// src/ScrollToTop.tsx
|
|
1507
|
-
var
|
|
1508
|
-
var
|
|
1509
|
-
var
|
|
2080
|
+
var import_react7 = require("react");
|
|
2081
|
+
var import_lucide_react11 = require("lucide-react");
|
|
2082
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1510
2083
|
function ScrollToTop() {
|
|
1511
|
-
const [visible, setVisible] = (0,
|
|
1512
|
-
(0,
|
|
2084
|
+
const [visible, setVisible] = (0, import_react7.useState)(false);
|
|
2085
|
+
(0, import_react7.useEffect)(() => {
|
|
1513
2086
|
const onScroll = () => setVisible(globalThis.scrollY > 300);
|
|
1514
2087
|
globalThis.addEventListener("scroll", onScroll, { passive: true });
|
|
1515
2088
|
return () => globalThis.removeEventListener("scroll", onScroll);
|
|
1516
2089
|
}, []);
|
|
1517
2090
|
if (!visible) return null;
|
|
1518
|
-
return /* @__PURE__ */ (0,
|
|
2091
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1519
2092
|
"button",
|
|
1520
2093
|
{
|
|
1521
2094
|
"aria-label": "Scroll to top",
|
|
1522
2095
|
onClick: () => globalThis.scrollTo({ top: 0, behavior: "smooth" }),
|
|
1523
2096
|
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,
|
|
2097
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react11.ArrowUp, { className: "h-5 w-5" })
|
|
1525
2098
|
}
|
|
1526
2099
|
);
|
|
1527
2100
|
}
|
|
1528
2101
|
|
|
1529
2102
|
// src/CommentsSection.tsx
|
|
1530
|
-
var
|
|
1531
|
-
var
|
|
2103
|
+
var import_react10 = require("next-auth/react");
|
|
2104
|
+
var import_react11 = require("react");
|
|
1532
2105
|
|
|
1533
2106
|
// src/CommentForm.tsx
|
|
1534
|
-
var
|
|
1535
|
-
var
|
|
2107
|
+
var import_react8 = require("react");
|
|
2108
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1536
2109
|
var MAX_LENGTH = 2e3;
|
|
1537
2110
|
var WARN_THRESHOLD = 1800;
|
|
1538
2111
|
function submitLabel(submitting, parentId) {
|
|
@@ -1540,9 +2113,9 @@ function submitLabel(submitting, parentId) {
|
|
|
1540
2113
|
return parentId ? "Reply" : "Post comment";
|
|
1541
2114
|
}
|
|
1542
2115
|
function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
1543
|
-
const [body, setBody] = (0,
|
|
1544
|
-
const [submitting, setSubmitting] = (0,
|
|
1545
|
-
const [error, setError] = (0,
|
|
2116
|
+
const [body, setBody] = (0, import_react8.useState)("");
|
|
2117
|
+
const [submitting, setSubmitting] = (0, import_react8.useState)(false);
|
|
2118
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
1546
2119
|
const remaining = MAX_LENGTH - body.length;
|
|
1547
2120
|
function handleSubmit(e) {
|
|
1548
2121
|
return __async(this, null, function* () {
|
|
@@ -1571,8 +2144,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1571
2144
|
}
|
|
1572
2145
|
});
|
|
1573
2146
|
}
|
|
1574
|
-
return /* @__PURE__ */ (0,
|
|
1575
|
-
/* @__PURE__ */ (0,
|
|
2147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
|
|
2148
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1576
2149
|
"textarea",
|
|
1577
2150
|
{
|
|
1578
2151
|
value: body,
|
|
@@ -1585,13 +2158,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1585
2158
|
"aria-label": parentId ? "Reply text" : "Comment text"
|
|
1586
2159
|
}
|
|
1587
2160
|
),
|
|
1588
|
-
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0,
|
|
2161
|
+
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
1589
2162
|
remaining,
|
|
1590
2163
|
" characters remaining"
|
|
1591
2164
|
] }),
|
|
1592
|
-
error && /* @__PURE__ */ (0,
|
|
1593
|
-
/* @__PURE__ */ (0,
|
|
1594
|
-
/* @__PURE__ */ (0,
|
|
2165
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-xs text-destructive", children: error }),
|
|
2166
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex gap-2", children: [
|
|
2167
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1595
2168
|
"button",
|
|
1596
2169
|
{
|
|
1597
2170
|
type: "submit",
|
|
@@ -1600,7 +2173,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1600
2173
|
children: submitLabel(submitting, parentId)
|
|
1601
2174
|
}
|
|
1602
2175
|
),
|
|
1603
|
-
onCancel && /* @__PURE__ */ (0,
|
|
2176
|
+
onCancel && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1604
2177
|
"button",
|
|
1605
2178
|
{
|
|
1606
2179
|
type: "button",
|
|
@@ -1614,8 +2187,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
1614
2187
|
}
|
|
1615
2188
|
|
|
1616
2189
|
// src/CommentItem.tsx
|
|
1617
|
-
var
|
|
1618
|
-
var
|
|
2190
|
+
var import_react9 = require("react");
|
|
2191
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1619
2192
|
function relativeTime(iso) {
|
|
1620
2193
|
const diff = Date.now() - new Date(iso).getTime();
|
|
1621
2194
|
const minutes = Math.floor(diff / 6e4);
|
|
@@ -1636,8 +2209,8 @@ function CommentItem({
|
|
|
1636
2209
|
isReply = false,
|
|
1637
2210
|
onRefresh
|
|
1638
2211
|
}) {
|
|
1639
|
-
const [showReplyForm, setShowReplyForm] = (0,
|
|
1640
|
-
const [deleting, setDeleting] = (0,
|
|
2212
|
+
const [showReplyForm, setShowReplyForm] = (0, import_react9.useState)(false);
|
|
2213
|
+
const [deleting, setDeleting] = (0, import_react9.useState)(false);
|
|
1641
2214
|
const replies = "replies" in comment ? comment.replies : [];
|
|
1642
2215
|
const canReply = !isReply && !!currentUserId && !comment.isDeleted;
|
|
1643
2216
|
const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted;
|
|
@@ -1653,15 +2226,15 @@ function CommentItem({
|
|
|
1653
2226
|
}
|
|
1654
2227
|
});
|
|
1655
2228
|
}
|
|
1656
|
-
return /* @__PURE__ */ (0,
|
|
1657
|
-
/* @__PURE__ */ (0,
|
|
1658
|
-
/* @__PURE__ */ (0,
|
|
1659
|
-
/* @__PURE__ */ (0,
|
|
1660
|
-
/* @__PURE__ */ (0,
|
|
2229
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
|
|
2230
|
+
/* @__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: [
|
|
2231
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2232
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
|
|
2233
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
|
|
1661
2234
|
] }),
|
|
1662
|
-
/* @__PURE__ */ (0,
|
|
1663
|
-
/* @__PURE__ */ (0,
|
|
1664
|
-
canReply && /* @__PURE__ */ (0,
|
|
2235
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
|
|
2236
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex gap-3 pt-1", children: [
|
|
2237
|
+
canReply && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1665
2238
|
"button",
|
|
1666
2239
|
{
|
|
1667
2240
|
onClick: () => setShowReplyForm((v) => !v),
|
|
@@ -1669,7 +2242,7 @@ function CommentItem({
|
|
|
1669
2242
|
children: showReplyForm ? "Cancel" : "Reply"
|
|
1670
2243
|
}
|
|
1671
2244
|
),
|
|
1672
|
-
canDelete && /* @__PURE__ */ (0,
|
|
2245
|
+
canDelete && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1673
2246
|
"button",
|
|
1674
2247
|
{
|
|
1675
2248
|
onClick: handleDelete,
|
|
@@ -1680,7 +2253,7 @@ function CommentItem({
|
|
|
1680
2253
|
)
|
|
1681
2254
|
] })
|
|
1682
2255
|
] }) }),
|
|
1683
|
-
showReplyForm && /* @__PURE__ */ (0,
|
|
2256
|
+
showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1684
2257
|
CommentForm,
|
|
1685
2258
|
{
|
|
1686
2259
|
articleSlug,
|
|
@@ -1692,7 +2265,7 @@ function CommentItem({
|
|
|
1692
2265
|
onCancel: () => setShowReplyForm(false)
|
|
1693
2266
|
}
|
|
1694
2267
|
) }),
|
|
1695
|
-
replies.length > 0 && /* @__PURE__ */ (0,
|
|
2268
|
+
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
2269
|
CommentItem,
|
|
1697
2270
|
{
|
|
1698
2271
|
comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
|
|
@@ -1707,7 +2280,7 @@ function CommentItem({
|
|
|
1707
2280
|
}
|
|
1708
2281
|
|
|
1709
2282
|
// src/CommentThread.tsx
|
|
1710
|
-
var
|
|
2283
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1711
2284
|
function CommentThread({
|
|
1712
2285
|
comments,
|
|
1713
2286
|
articleSlug,
|
|
@@ -1715,9 +2288,9 @@ function CommentThread({
|
|
|
1715
2288
|
onRefresh
|
|
1716
2289
|
}) {
|
|
1717
2290
|
if (comments.length === 0) {
|
|
1718
|
-
return /* @__PURE__ */ (0,
|
|
2291
|
+
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
2292
|
}
|
|
1720
|
-
return /* @__PURE__ */ (0,
|
|
2293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1721
2294
|
CommentItem,
|
|
1722
2295
|
{
|
|
1723
2296
|
comment,
|
|
@@ -1730,17 +2303,17 @@ function CommentThread({
|
|
|
1730
2303
|
}
|
|
1731
2304
|
|
|
1732
2305
|
// src/CommentsSection.tsx
|
|
1733
|
-
var
|
|
2306
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1734
2307
|
function CommentsSection({ articleSlug, config }) {
|
|
1735
2308
|
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,
|
|
2309
|
+
const { data: session, status } = (0, import_react10.useSession)();
|
|
2310
|
+
const [comments, setComments] = (0, import_react11.useState)([]);
|
|
2311
|
+
const [loading, setLoading] = (0, import_react11.useState)(true);
|
|
2312
|
+
const [fetchError, setFetchError] = (0, import_react11.useState)(null);
|
|
1740
2313
|
const perArticleEnabled = (_b = (_a = config.perArticleOverride) == null ? void 0 : _a[articleSlug]) != null ? _b : true;
|
|
1741
2314
|
const enabled = config.enabled && perArticleEnabled;
|
|
1742
2315
|
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,
|
|
2316
|
+
const fetchComments = (0, import_react11.useCallback)(() => __async(this, null, function* () {
|
|
1744
2317
|
setLoading(true);
|
|
1745
2318
|
setFetchError(null);
|
|
1746
2319
|
try {
|
|
@@ -1754,7 +2327,7 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1754
2327
|
setLoading(false);
|
|
1755
2328
|
}
|
|
1756
2329
|
}), [articleSlug]);
|
|
1757
|
-
(0,
|
|
2330
|
+
(0, import_react11.useEffect)(() => {
|
|
1758
2331
|
if (enabled) {
|
|
1759
2332
|
fetchComments().catch(() => void 0);
|
|
1760
2333
|
}
|
|
@@ -1762,13 +2335,13 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1762
2335
|
if (!enabled) return null;
|
|
1763
2336
|
const count = comments.length;
|
|
1764
2337
|
const label = count === 1 ? "1 comment" : `${count} comments`;
|
|
1765
|
-
return /* @__PURE__ */ (0,
|
|
1766
|
-
/* @__PURE__ */ (0,
|
|
1767
|
-
status === "authenticated" ? /* @__PURE__ */ (0,
|
|
1768
|
-
/* @__PURE__ */ (0,
|
|
2338
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
|
|
2339
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
|
|
2340
|
+
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: [
|
|
2341
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1769
2342
|
"button",
|
|
1770
2343
|
{
|
|
1771
|
-
onClick: () => (0,
|
|
2344
|
+
onClick: () => (0, import_react10.signIn)(),
|
|
1772
2345
|
className: "text-primary underline hover:no-underline focus:outline-none",
|
|
1773
2346
|
children: "Sign in"
|
|
1774
2347
|
}
|
|
@@ -1776,8 +2349,8 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1776
2349
|
" ",
|
|
1777
2350
|
"to join the discussion."
|
|
1778
2351
|
] }),
|
|
1779
|
-
fetchError && /* @__PURE__ */ (0,
|
|
1780
|
-
loading ? /* @__PURE__ */ (0,
|
|
2352
|
+
fetchError && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
|
|
2353
|
+
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
2354
|
CommentThread,
|
|
1782
2355
|
{
|
|
1783
2356
|
comments,
|
|
@@ -1796,10 +2369,10 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1796
2369
|
ArticleDetailHero,
|
|
1797
2370
|
ArticleNavigation,
|
|
1798
2371
|
ArticleSEO,
|
|
1799
|
-
ArticleSchema,
|
|
1800
2372
|
ArticleSearchBar,
|
|
1801
2373
|
ArticleSocialShare,
|
|
1802
2374
|
ArticleTOC,
|
|
2375
|
+
ArticleViewTracker,
|
|
1803
2376
|
ArticlesHero,
|
|
1804
2377
|
ArticlesPage,
|
|
1805
2378
|
AuthorArticlesPage,
|
|
@@ -1814,6 +2387,7 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1814
2387
|
CommentItem,
|
|
1815
2388
|
CommentThread,
|
|
1816
2389
|
CommentsSection,
|
|
2390
|
+
CtaViewTracker,
|
|
1817
2391
|
DEFAULT_CATEGORIES_PAGE_SIZE,
|
|
1818
2392
|
DEFAULT_LAYOUT,
|
|
1819
2393
|
DEFAULT_PAGE_SIZE,
|
|
@@ -1821,7 +2395,10 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1821
2395
|
FeaturedArticle,
|
|
1822
2396
|
LatestArticles,
|
|
1823
2397
|
LatestArticlesSection,
|
|
2398
|
+
PaginationNav,
|
|
2399
|
+
RelatedArticlesSection,
|
|
1824
2400
|
ScrollToTop,
|
|
2401
|
+
SeriesArticlesPage,
|
|
1825
2402
|
breadcrumbsAreEnabled,
|
|
1826
2403
|
getBreadcrumbsConfig,
|
|
1827
2404
|
useArticles
|