@fullstackdatasolutions/articles 0.3.0 → 0.4.2
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/README.md +76 -5
- package/dist/index.cjs +287 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +280 -100
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +2 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/package.json +3 -2
- package/src/ArticleDetailHero.tsx +31 -5
- package/src/ArticleNavigation.tsx +56 -0
- package/src/ArticleSocialShare.tsx +96 -0
- package/src/CategoryArticlesPage.tsx +21 -4
- package/src/__tests__/ArticleCategoryGrid.test.tsx +34 -1
- package/src/__tests__/ArticleDetailHero.test.tsx +42 -10
- package/src/__tests__/ArticleNavigation.test.tsx +127 -0
- package/src/__tests__/ArticleSocialShare.test.tsx +187 -0
- package/src/__tests__/ArticlesPage.test.tsx +147 -1
- package/src/__tests__/CommentItem.test.tsx +110 -0
- package/src/__tests__/seoUtils.test.ts +35 -0
- package/src/index.ts +3 -0
- package/src/seoUtils.ts +7 -1
package/dist/index.js
CHANGED
|
@@ -615,7 +615,11 @@ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
|
615
615
|
function toSlug(cat) {
|
|
616
616
|
return cat.toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-]/g, "");
|
|
617
617
|
}
|
|
618
|
-
function ArticleDetailHero({
|
|
618
|
+
function ArticleDetailHero({
|
|
619
|
+
article,
|
|
620
|
+
categoryBasePath = "/articles/category",
|
|
621
|
+
showDate = false
|
|
622
|
+
}) {
|
|
619
623
|
return /* @__PURE__ */ jsxs9(
|
|
620
624
|
"section",
|
|
621
625
|
{
|
|
@@ -636,6 +640,7 @@ function ArticleDetailHero({ article, categoryBasePath }) {
|
|
|
636
640
|
fill: true,
|
|
637
641
|
sizes: "100vw",
|
|
638
642
|
className: "object-cover",
|
|
643
|
+
style: { objectFit: "cover" },
|
|
639
644
|
priority: true
|
|
640
645
|
}
|
|
641
646
|
),
|
|
@@ -663,42 +668,67 @@ function ArticleDetailHero({ article, categoryBasePath }) {
|
|
|
663
668
|
width: "100%"
|
|
664
669
|
},
|
|
665
670
|
children: [
|
|
666
|
-
article.categories && article.categories.length > 0 && /* @__PURE__ */ jsx10(
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
{
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
) : /* @__PURE__ */ jsx10(
|
|
676
|
-
"span",
|
|
677
|
-
{
|
|
678
|
-
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full",
|
|
679
|
-
children: cat
|
|
671
|
+
article.categories && article.categories.length > 0 && /* @__PURE__ */ jsx10(
|
|
672
|
+
"div",
|
|
673
|
+
{
|
|
674
|
+
style: {
|
|
675
|
+
display: "flex",
|
|
676
|
+
flexWrap: "wrap",
|
|
677
|
+
justifyContent: "center",
|
|
678
|
+
gap: "0.5rem",
|
|
679
|
+
marginBottom: "1rem"
|
|
680
680
|
},
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
681
|
+
children: article.categories.slice(0, 4).map(
|
|
682
|
+
(cat) => categoryBasePath ? /* @__PURE__ */ jsx10(
|
|
683
|
+
Link5,
|
|
684
|
+
{
|
|
685
|
+
href: `${categoryBasePath}/${toSlug(cat)}`,
|
|
686
|
+
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full hover:bg-white/30 transition-colors",
|
|
687
|
+
children: cat
|
|
688
|
+
},
|
|
689
|
+
cat
|
|
690
|
+
) : /* @__PURE__ */ jsx10(
|
|
691
|
+
"span",
|
|
692
|
+
{
|
|
693
|
+
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full",
|
|
694
|
+
children: cat
|
|
695
|
+
},
|
|
696
|
+
cat
|
|
697
|
+
)
|
|
698
|
+
)
|
|
699
|
+
}
|
|
700
|
+
),
|
|
684
701
|
/* @__PURE__ */ jsx10("h1", { className: "text-4xl md:text-5xl font-bold mb-4", children: article.title }),
|
|
685
|
-
/* @__PURE__ */ jsxs9(
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
"
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
+
/* @__PURE__ */ jsxs9(
|
|
703
|
+
"div",
|
|
704
|
+
{
|
|
705
|
+
style: {
|
|
706
|
+
display: "flex",
|
|
707
|
+
alignItems: "center",
|
|
708
|
+
justifyContent: "center",
|
|
709
|
+
gap: "1.5rem",
|
|
710
|
+
fontSize: "0.875rem",
|
|
711
|
+
color: "rgba(255,255,255,0.8)"
|
|
712
|
+
},
|
|
713
|
+
children: [
|
|
714
|
+
showDate && article.date && /* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
715
|
+
/* @__PURE__ */ jsx10(Calendar3, { className: "h-4 w-4" }),
|
|
716
|
+
/* @__PURE__ */ jsx10("span", { children: article.date })
|
|
717
|
+
] }),
|
|
718
|
+
/* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
719
|
+
/* @__PURE__ */ jsx10(Clock3, { className: "h-4 w-4" }),
|
|
720
|
+
/* @__PURE__ */ jsx10("span", { children: article.readTime })
|
|
721
|
+
] }),
|
|
722
|
+
/* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
723
|
+
/* @__PURE__ */ jsx10(User2, { className: "h-4 w-4" }),
|
|
724
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
725
|
+
"By ",
|
|
726
|
+
article.author
|
|
727
|
+
] })
|
|
728
|
+
] })
|
|
729
|
+
]
|
|
730
|
+
}
|
|
731
|
+
)
|
|
702
732
|
]
|
|
703
733
|
}
|
|
704
734
|
)
|
|
@@ -737,30 +767,55 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
737
767
|
]
|
|
738
768
|
}
|
|
739
769
|
),
|
|
740
|
-
/* @__PURE__ */ jsxs10(
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
{
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
770
|
+
/* @__PURE__ */ jsxs10(
|
|
771
|
+
"section",
|
|
772
|
+
{
|
|
773
|
+
style: {
|
|
774
|
+
position: "relative",
|
|
775
|
+
height: "20rem",
|
|
776
|
+
display: "flex",
|
|
777
|
+
alignItems: "center",
|
|
778
|
+
justifyContent: "center",
|
|
779
|
+
overflow: "hidden"
|
|
780
|
+
},
|
|
781
|
+
children: [
|
|
782
|
+
/* @__PURE__ */ jsx11(
|
|
783
|
+
Image5,
|
|
784
|
+
{
|
|
785
|
+
src: heroImage,
|
|
786
|
+
alt: categoryName,
|
|
787
|
+
fill: true,
|
|
788
|
+
sizes: "100vw",
|
|
789
|
+
style: { objectFit: "cover" },
|
|
790
|
+
priority: true
|
|
791
|
+
}
|
|
792
|
+
),
|
|
793
|
+
/* @__PURE__ */ jsx11("div", { style: { position: "absolute", inset: 0, backgroundColor: "rgba(0,0,0,0.6)" } }),
|
|
794
|
+
/* @__PURE__ */ jsxs10(
|
|
795
|
+
"div",
|
|
796
|
+
{
|
|
797
|
+
style: {
|
|
798
|
+
position: "relative",
|
|
799
|
+
zIndex: 10,
|
|
800
|
+
textAlign: "center",
|
|
801
|
+
color: "white",
|
|
802
|
+
padding: "0 1rem"
|
|
803
|
+
},
|
|
804
|
+
children: [
|
|
805
|
+
/* @__PURE__ */ jsx11("p", { className: "text-sm font-semibold uppercase tracking-widest mb-3 opacity-80", children: "Category" }),
|
|
806
|
+
/* @__PURE__ */ jsx11("h1", { className: "text-4xl md:text-5xl font-bold mb-3", children: categoryName }),
|
|
807
|
+
description.long && /* @__PURE__ */ jsx11("p", { className: "text-lg opacity-90 max-w-2xl mx-auto mt-2", children: description.long }),
|
|
808
|
+
/* @__PURE__ */ jsxs10("p", { className: "text-lg opacity-70 mt-2", children: [
|
|
809
|
+
articles.length,
|
|
810
|
+
" article",
|
|
811
|
+
articles.length === 1 ? "" : "s"
|
|
812
|
+
] })
|
|
813
|
+
]
|
|
814
|
+
}
|
|
815
|
+
)
|
|
816
|
+
]
|
|
817
|
+
}
|
|
818
|
+
),
|
|
764
819
|
/* @__PURE__ */ jsx11("section", { className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ jsxs10("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
765
820
|
/* @__PURE__ */ jsxs10("div", { className: "mb-8 flex items-center justify-between", children: [
|
|
766
821
|
/* @__PURE__ */ jsx11(Link6, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
|
|
@@ -771,13 +826,136 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
771
826
|
] });
|
|
772
827
|
}
|
|
773
828
|
|
|
829
|
+
// src/ArticleSocialShare.tsx
|
|
830
|
+
import { useState as useState4 } from "react";
|
|
831
|
+
import { Share2, Copy, Check, Mail, MessageCircle, Users, FileText } from "lucide-react";
|
|
832
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
833
|
+
function ArticleSocialShare({ title, url, excerpt, shareMessage }) {
|
|
834
|
+
const [copied, setCopied] = useState4(false);
|
|
835
|
+
const encodedTitle = encodeURIComponent(title);
|
|
836
|
+
const encodedUrl = encodeURIComponent(url);
|
|
837
|
+
const encodedExcerpt = encodeURIComponent(excerpt || "");
|
|
838
|
+
const shareLinks = {
|
|
839
|
+
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
|
|
840
|
+
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
|
|
841
|
+
twitter: `https://twitter.com/intent/tweet?text=${encodedTitle}&url=${encodedUrl}`,
|
|
842
|
+
reddit: `https://reddit.com/submit?url=${encodedUrl}&title=${encodedTitle}`,
|
|
843
|
+
email: `mailto:?subject=${encodedTitle}&body=${encodedExcerpt}%0A%0A${encodedUrl}`,
|
|
844
|
+
whatsapp: `https://wa.me/?text=${encodedTitle}%20${encodedUrl}`,
|
|
845
|
+
telegram: `https://t.me/share/url?url=${encodedUrl}&text=${encodedTitle}`
|
|
846
|
+
};
|
|
847
|
+
const copyToClipboard = () => __async(this, null, function* () {
|
|
848
|
+
try {
|
|
849
|
+
yield navigator.clipboard.writeText(url);
|
|
850
|
+
setCopied(true);
|
|
851
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
852
|
+
} catch (e) {
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
const openShareWindow = (shareUrl) => {
|
|
856
|
+
window.open(shareUrl, "_blank", "width=600,height=400,scrollbars=yes,resizable=yes");
|
|
857
|
+
};
|
|
858
|
+
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";
|
|
859
|
+
return /* @__PURE__ */ jsxs11("div", { className: "border-t border-border pt-8 mt-8", children: [
|
|
860
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 mb-4", children: [
|
|
861
|
+
/* @__PURE__ */ jsx12(Share2, { className: "h-5 w-5 text-muted-foreground" }),
|
|
862
|
+
/* @__PURE__ */ jsx12("h3", { className: "text-lg font-semibold text-foreground", children: "Share this article" })
|
|
863
|
+
] }),
|
|
864
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap gap-2", children: [
|
|
865
|
+
/* @__PURE__ */ jsxs11("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.linkedin), children: [
|
|
866
|
+
/* @__PURE__ */ jsx12(Users, { className: "h-4 w-4" }),
|
|
867
|
+
"LinkedIn"
|
|
868
|
+
] }),
|
|
869
|
+
/* @__PURE__ */ jsxs11("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.facebook), children: [
|
|
870
|
+
/* @__PURE__ */ jsx12("span", { className: "text-xs font-bold", children: "f" }),
|
|
871
|
+
" Facebook"
|
|
872
|
+
] }),
|
|
873
|
+
/* @__PURE__ */ jsxs11("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.twitter), children: [
|
|
874
|
+
/* @__PURE__ */ jsx12(MessageCircle, { className: "h-4 w-4" }),
|
|
875
|
+
"Twitter"
|
|
876
|
+
] }),
|
|
877
|
+
/* @__PURE__ */ jsxs11("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.reddit), children: [
|
|
878
|
+
/* @__PURE__ */ jsx12(FileText, { className: "h-4 w-4" }),
|
|
879
|
+
"Reddit"
|
|
880
|
+
] }),
|
|
881
|
+
/* @__PURE__ */ jsxs11("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.whatsapp), children: [
|
|
882
|
+
/* @__PURE__ */ jsx12(MessageCircle, { className: "h-4 w-4" }),
|
|
883
|
+
"WhatsApp"
|
|
884
|
+
] }),
|
|
885
|
+
/* @__PURE__ */ jsxs11("button", { className: btnClass, onClick: () => openShareWindow(shareLinks.telegram), children: [
|
|
886
|
+
/* @__PURE__ */ jsx12(MessageCircle, { className: "h-4 w-4" }),
|
|
887
|
+
"Telegram"
|
|
888
|
+
] }),
|
|
889
|
+
/* @__PURE__ */ jsxs11(
|
|
890
|
+
"button",
|
|
891
|
+
{
|
|
892
|
+
className: btnClass,
|
|
893
|
+
onClick: () => {
|
|
894
|
+
globalThis.location.href = shareLinks.email;
|
|
895
|
+
},
|
|
896
|
+
children: [
|
|
897
|
+
/* @__PURE__ */ jsx12(Mail, { className: "h-4 w-4" }),
|
|
898
|
+
"Email"
|
|
899
|
+
]
|
|
900
|
+
}
|
|
901
|
+
),
|
|
902
|
+
/* @__PURE__ */ jsxs11("button", { className: btnClass, onClick: copyToClipboard, children: [
|
|
903
|
+
copied ? /* @__PURE__ */ jsx12(Check, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx12(Copy, { className: "h-4 w-4" }),
|
|
904
|
+
copied ? "Copied!" : "Copy Link"
|
|
905
|
+
] })
|
|
906
|
+
] }),
|
|
907
|
+
shareMessage && /* @__PURE__ */ jsx12("p", { className: "text-sm text-muted-foreground mt-3", children: shareMessage })
|
|
908
|
+
] });
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// src/ArticleNavigation.tsx
|
|
912
|
+
import Link7 from "next/link";
|
|
913
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
914
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
915
|
+
function truncateTitle(title, maxLength = 60) {
|
|
916
|
+
return title.length > maxLength ? `${title.substring(0, maxLength)}...` : title;
|
|
917
|
+
}
|
|
918
|
+
function ArticleNavigation({ previous, next, basePath }) {
|
|
919
|
+
if (!previous && !next) return null;
|
|
920
|
+
return /* @__PURE__ */ jsx13("nav", { className: "mt-12 pt-8 border-t border-border", "aria-label": "Article navigation", children: /* @__PURE__ */ jsxs12("div", { className: "flex justify-between gap-4", children: [
|
|
921
|
+
/* @__PURE__ */ jsx13("div", { className: "flex-1 min-w-0", children: previous && /* @__PURE__ */ jsxs12(
|
|
922
|
+
Link7,
|
|
923
|
+
{
|
|
924
|
+
href: `${basePath}/${previous.slug}`,
|
|
925
|
+
className: "group flex items-center gap-3 p-4 rounded-lg border border-border hover:border-primary/20 hover:bg-muted/50 transition-colors",
|
|
926
|
+
children: [
|
|
927
|
+
/* @__PURE__ */ jsx13(ChevronLeft, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" }),
|
|
928
|
+
/* @__PURE__ */ jsxs12("div", { className: "min-w-0 flex-1", children: [
|
|
929
|
+
/* @__PURE__ */ jsx13("div", { className: "text-sm text-muted-foreground mb-1", children: "Previous Article" }),
|
|
930
|
+
/* @__PURE__ */ jsx13("div", { className: "font-medium text-foreground truncate", title: previous.title, children: truncateTitle(previous.title) })
|
|
931
|
+
] })
|
|
932
|
+
]
|
|
933
|
+
}
|
|
934
|
+
) }),
|
|
935
|
+
/* @__PURE__ */ jsx13("div", { className: "flex-1 min-w-0", children: next && /* @__PURE__ */ jsxs12(
|
|
936
|
+
Link7,
|
|
937
|
+
{
|
|
938
|
+
href: `${basePath}/${next.slug}`,
|
|
939
|
+
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",
|
|
940
|
+
children: [
|
|
941
|
+
/* @__PURE__ */ jsxs12("div", { className: "min-w-0 flex-1 text-right", children: [
|
|
942
|
+
/* @__PURE__ */ jsx13("div", { className: "text-sm text-muted-foreground mb-1", children: "Next Article" }),
|
|
943
|
+
/* @__PURE__ */ jsx13("div", { className: "font-medium text-foreground truncate", title: next.title, children: truncateTitle(next.title) })
|
|
944
|
+
] }),
|
|
945
|
+
/* @__PURE__ */ jsx13(ChevronRight, { className: "h-5 w-5 text-muted-foreground group-hover:text-primary shrink-0" })
|
|
946
|
+
]
|
|
947
|
+
}
|
|
948
|
+
) })
|
|
949
|
+
] }) });
|
|
950
|
+
}
|
|
951
|
+
|
|
774
952
|
// src/CommentsSection.tsx
|
|
775
953
|
import { useSession, signIn } from "next-auth/react";
|
|
776
|
-
import { useCallback as useCallback2, useEffect as useEffect4, useState as
|
|
954
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useState as useState7 } from "react";
|
|
777
955
|
|
|
778
956
|
// src/CommentForm.tsx
|
|
779
|
-
import { useState as
|
|
780
|
-
import { jsx as
|
|
957
|
+
import { useState as useState5 } from "react";
|
|
958
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
781
959
|
var MAX_LENGTH = 2e3;
|
|
782
960
|
var WARN_THRESHOLD = 1800;
|
|
783
961
|
function submitLabel(submitting, parentId) {
|
|
@@ -785,9 +963,9 @@ function submitLabel(submitting, parentId) {
|
|
|
785
963
|
return parentId ? "Reply" : "Post comment";
|
|
786
964
|
}
|
|
787
965
|
function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
788
|
-
const [body, setBody] =
|
|
789
|
-
const [submitting, setSubmitting] =
|
|
790
|
-
const [error, setError] =
|
|
966
|
+
const [body, setBody] = useState5("");
|
|
967
|
+
const [submitting, setSubmitting] = useState5(false);
|
|
968
|
+
const [error, setError] = useState5(null);
|
|
791
969
|
const remaining = MAX_LENGTH - body.length;
|
|
792
970
|
function handleSubmit(e) {
|
|
793
971
|
return __async(this, null, function* () {
|
|
@@ -816,8 +994,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
816
994
|
}
|
|
817
995
|
});
|
|
818
996
|
}
|
|
819
|
-
return /* @__PURE__ */
|
|
820
|
-
/* @__PURE__ */
|
|
997
|
+
return /* @__PURE__ */ jsxs13("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
|
|
998
|
+
/* @__PURE__ */ jsx14(
|
|
821
999
|
"textarea",
|
|
822
1000
|
{
|
|
823
1001
|
value: body,
|
|
@@ -830,13 +1008,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
830
1008
|
"aria-label": parentId ? "Reply text" : "Comment text"
|
|
831
1009
|
}
|
|
832
1010
|
),
|
|
833
|
-
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */
|
|
1011
|
+
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ jsxs13("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
834
1012
|
remaining,
|
|
835
1013
|
" characters remaining"
|
|
836
1014
|
] }),
|
|
837
|
-
error && /* @__PURE__ */
|
|
838
|
-
/* @__PURE__ */
|
|
839
|
-
/* @__PURE__ */
|
|
1015
|
+
error && /* @__PURE__ */ jsx14("p", { className: "text-xs text-destructive", children: error }),
|
|
1016
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex gap-2", children: [
|
|
1017
|
+
/* @__PURE__ */ jsx14(
|
|
840
1018
|
"button",
|
|
841
1019
|
{
|
|
842
1020
|
type: "submit",
|
|
@@ -845,7 +1023,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
845
1023
|
children: submitLabel(submitting, parentId)
|
|
846
1024
|
}
|
|
847
1025
|
),
|
|
848
|
-
onCancel && /* @__PURE__ */
|
|
1026
|
+
onCancel && /* @__PURE__ */ jsx14(
|
|
849
1027
|
"button",
|
|
850
1028
|
{
|
|
851
1029
|
type: "button",
|
|
@@ -859,8 +1037,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
859
1037
|
}
|
|
860
1038
|
|
|
861
1039
|
// src/CommentItem.tsx
|
|
862
|
-
import { useState as
|
|
863
|
-
import { Fragment, jsx as
|
|
1040
|
+
import { useState as useState6 } from "react";
|
|
1041
|
+
import { Fragment, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
864
1042
|
function relativeTime(iso) {
|
|
865
1043
|
const diff = Date.now() - new Date(iso).getTime();
|
|
866
1044
|
const minutes = Math.floor(diff / 6e4);
|
|
@@ -881,8 +1059,8 @@ function CommentItem({
|
|
|
881
1059
|
isReply = false,
|
|
882
1060
|
onRefresh
|
|
883
1061
|
}) {
|
|
884
|
-
const [showReplyForm, setShowReplyForm] =
|
|
885
|
-
const [deleting, setDeleting] =
|
|
1062
|
+
const [showReplyForm, setShowReplyForm] = useState6(false);
|
|
1063
|
+
const [deleting, setDeleting] = useState6(false);
|
|
886
1064
|
const replies = "replies" in comment ? comment.replies : [];
|
|
887
1065
|
const canReply = !isReply && !!currentUserId && !comment.isDeleted;
|
|
888
1066
|
const canDelete = !!currentUserId && currentUserId === comment.authorId && !comment.isDeleted;
|
|
@@ -898,15 +1076,15 @@ function CommentItem({
|
|
|
898
1076
|
}
|
|
899
1077
|
});
|
|
900
1078
|
}
|
|
901
|
-
return /* @__PURE__ */
|
|
902
|
-
/* @__PURE__ */
|
|
903
|
-
/* @__PURE__ */
|
|
904
|
-
/* @__PURE__ */
|
|
905
|
-
/* @__PURE__ */
|
|
1079
|
+
return /* @__PURE__ */ jsxs14("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
|
|
1080
|
+
/* @__PURE__ */ jsx15("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ jsx15("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ jsxs14(Fragment, { children: [
|
|
1081
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1082
|
+
/* @__PURE__ */ jsx15("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
|
|
1083
|
+
/* @__PURE__ */ jsx15("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
|
|
906
1084
|
] }),
|
|
907
|
-
/* @__PURE__ */
|
|
908
|
-
/* @__PURE__ */
|
|
909
|
-
canReply && /* @__PURE__ */
|
|
1085
|
+
/* @__PURE__ */ jsx15("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
|
|
1086
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex gap-3 pt-1", children: [
|
|
1087
|
+
canReply && /* @__PURE__ */ jsx15(
|
|
910
1088
|
"button",
|
|
911
1089
|
{
|
|
912
1090
|
onClick: () => setShowReplyForm((v) => !v),
|
|
@@ -914,7 +1092,7 @@ function CommentItem({
|
|
|
914
1092
|
children: showReplyForm ? "Cancel" : "Reply"
|
|
915
1093
|
}
|
|
916
1094
|
),
|
|
917
|
-
canDelete && /* @__PURE__ */
|
|
1095
|
+
canDelete && /* @__PURE__ */ jsx15(
|
|
918
1096
|
"button",
|
|
919
1097
|
{
|
|
920
1098
|
onClick: handleDelete,
|
|
@@ -925,7 +1103,7 @@ function CommentItem({
|
|
|
925
1103
|
)
|
|
926
1104
|
] })
|
|
927
1105
|
] }) }),
|
|
928
|
-
showReplyForm && /* @__PURE__ */
|
|
1106
|
+
showReplyForm && /* @__PURE__ */ jsx15("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ jsx15(
|
|
929
1107
|
CommentForm,
|
|
930
1108
|
{
|
|
931
1109
|
articleSlug,
|
|
@@ -937,7 +1115,7 @@ function CommentItem({
|
|
|
937
1115
|
onCancel: () => setShowReplyForm(false)
|
|
938
1116
|
}
|
|
939
1117
|
) }),
|
|
940
|
-
replies.length > 0 && /* @__PURE__ */
|
|
1118
|
+
replies.length > 0 && /* @__PURE__ */ jsx15("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ jsx15(
|
|
941
1119
|
CommentItem,
|
|
942
1120
|
{
|
|
943
1121
|
comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
|
|
@@ -952,7 +1130,7 @@ function CommentItem({
|
|
|
952
1130
|
}
|
|
953
1131
|
|
|
954
1132
|
// src/CommentThread.tsx
|
|
955
|
-
import { jsx as
|
|
1133
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
956
1134
|
function CommentThread({
|
|
957
1135
|
comments,
|
|
958
1136
|
articleSlug,
|
|
@@ -960,9 +1138,9 @@ function CommentThread({
|
|
|
960
1138
|
onRefresh
|
|
961
1139
|
}) {
|
|
962
1140
|
if (comments.length === 0) {
|
|
963
|
-
return /* @__PURE__ */
|
|
1141
|
+
return /* @__PURE__ */ jsx16("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
|
|
964
1142
|
}
|
|
965
|
-
return /* @__PURE__ */
|
|
1143
|
+
return /* @__PURE__ */ jsx16("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ jsx16(
|
|
966
1144
|
CommentItem,
|
|
967
1145
|
{
|
|
968
1146
|
comment,
|
|
@@ -975,13 +1153,13 @@ function CommentThread({
|
|
|
975
1153
|
}
|
|
976
1154
|
|
|
977
1155
|
// src/CommentsSection.tsx
|
|
978
|
-
import { jsx as
|
|
1156
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
979
1157
|
function CommentsSection({ articleSlug, config }) {
|
|
980
1158
|
var _a, _b, _c, _d, _e, _f;
|
|
981
1159
|
const { data: session, status } = useSession();
|
|
982
|
-
const [comments, setComments] =
|
|
983
|
-
const [loading, setLoading] =
|
|
984
|
-
const [fetchError, setFetchError] =
|
|
1160
|
+
const [comments, setComments] = useState7([]);
|
|
1161
|
+
const [loading, setLoading] = useState7(true);
|
|
1162
|
+
const [fetchError, setFetchError] = useState7(null);
|
|
985
1163
|
const perArticleEnabled = (_b = (_a = config.perArticleOverride) == null ? void 0 : _a[articleSlug]) != null ? _b : true;
|
|
986
1164
|
const enabled = config.enabled && perArticleEnabled;
|
|
987
1165
|
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;
|
|
@@ -1007,10 +1185,10 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1007
1185
|
if (!enabled) return null;
|
|
1008
1186
|
const count = comments.length;
|
|
1009
1187
|
const label = count === 1 ? "1 comment" : `${count} comments`;
|
|
1010
|
-
return /* @__PURE__ */
|
|
1011
|
-
/* @__PURE__ */
|
|
1012
|
-
status === "authenticated" ? /* @__PURE__ */
|
|
1013
|
-
/* @__PURE__ */
|
|
1188
|
+
return /* @__PURE__ */ jsxs15("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
|
|
1189
|
+
/* @__PURE__ */ jsx17("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
|
|
1190
|
+
status === "authenticated" ? /* @__PURE__ */ jsx17(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ jsxs15("p", { className: "text-sm text-muted-foreground", children: [
|
|
1191
|
+
/* @__PURE__ */ jsx17(
|
|
1014
1192
|
"button",
|
|
1015
1193
|
{
|
|
1016
1194
|
onClick: () => signIn(),
|
|
@@ -1021,8 +1199,8 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1021
1199
|
" ",
|
|
1022
1200
|
"to join the discussion."
|
|
1023
1201
|
] }),
|
|
1024
|
-
fetchError && /* @__PURE__ */
|
|
1025
|
-
loading ? /* @__PURE__ */
|
|
1202
|
+
fetchError && /* @__PURE__ */ jsx17("p", { className: "text-sm text-destructive", children: fetchError }),
|
|
1203
|
+
loading ? /* @__PURE__ */ jsx17("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ jsx17(
|
|
1026
1204
|
CommentThread,
|
|
1027
1205
|
{
|
|
1028
1206
|
comments,
|
|
@@ -1037,8 +1215,10 @@ export {
|
|
|
1037
1215
|
ArticleCard,
|
|
1038
1216
|
ArticleCategoryGrid,
|
|
1039
1217
|
ArticleDetailHero,
|
|
1218
|
+
ArticleNavigation,
|
|
1040
1219
|
ArticleSchema,
|
|
1041
1220
|
ArticleSearchBar,
|
|
1221
|
+
ArticleSocialShare,
|
|
1042
1222
|
ArticlesHero,
|
|
1043
1223
|
ArticlesPage,
|
|
1044
1224
|
BreadcrumbSchema,
|