@fullstackdatasolutions/articles 0.2.1 → 0.3.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/README.md +24 -1
- package/dist/index.cjs +169 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +168 -78
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/ArticleCategoryGrid.tsx +1 -8
- package/src/ArticleDetailHero.tsx +96 -0
- package/src/__tests__/ArticleCategoryGrid.test.tsx +6 -0
- package/src/__tests__/ArticleDetailHero.test.tsx +102 -0
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -171,6 +171,29 @@ hero: {
|
|
|
171
171
|
|
|
172
172
|
---
|
|
173
173
|
|
|
174
|
+
## ArticleDetailHero
|
|
175
|
+
|
|
176
|
+
`ArticleDetailHero` renders the article's featured image full-width behind the title, category tags, and metadata (date, read time, author). Use it in your `app/articles/[slug]/page.tsx` instead of building a custom hero.
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
import { ArticleDetailHero } from '@fullstackdatasolutions/articles'
|
|
180
|
+
|
|
181
|
+
<ArticleDetailHero
|
|
182
|
+
article={article}
|
|
183
|
+
categoryBasePath="/articles/category"
|
|
184
|
+
/>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
| Prop | Type | Required | Description |
|
|
188
|
+
|---|---|---|---|
|
|
189
|
+
| `article` | `Article` | yes | Article object returned by `getArticleMetadata` |
|
|
190
|
+
| `categoryBasePath` | `string` | no | Base path for category links (e.g. `"/articles/category"`). Omit to render categories as plain text. |
|
|
191
|
+
|
|
192
|
+
- Category tags are capped at 4. When an article has more than 4 categories, only the first 4 are shown.
|
|
193
|
+
- All layout styles are inline so the component renders correctly regardless of whether `@source` is configured for the package.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
174
197
|
## Comments setup
|
|
175
198
|
|
|
176
199
|
### 1. Enable in config
|
|
@@ -234,7 +257,7 @@ JSON-LD structured data (`ArticleSchema`, `BreadcrumbSchema`, `CollectionPageSch
|
|
|
234
257
|
| `title` | `string` | yes | Article title |
|
|
235
258
|
| `excerpt` | `string` | yes | One-sentence summary for cards and meta |
|
|
236
259
|
| `author` | `string` | no | Defaults to `'Andrew Blase'` |
|
|
237
|
-
| `tags` | `string[]` | no | Used as categories. First tag = primary category. |
|
|
260
|
+
| `tags` | `string[]` | no | Used as categories. First tag = primary category. ArticleDetailHero displays at most 4 category tags; articles with more than 4 show only the first 4 in the hero. |
|
|
238
261
|
| `date` | `YYYY-MM-DD` | no | Omit to publish immediately. Future dates hide until that date. |
|
|
239
262
|
| `featuredImage` | `string` | no | Filename relative to the article directory. Auto-detected if omitted. |
|
|
240
263
|
|
package/dist/index.cjs
CHANGED
|
@@ -71,6 +71,7 @@ var src_exports = {};
|
|
|
71
71
|
__export(src_exports, {
|
|
72
72
|
ArticleCard: () => ArticleCard,
|
|
73
73
|
ArticleCategoryGrid: () => ArticleCategoryGrid,
|
|
74
|
+
ArticleDetailHero: () => ArticleDetailHero,
|
|
74
75
|
ArticleSchema: () => ArticleSchema,
|
|
75
76
|
ArticleSearchBar: () => ArticleSearchBar,
|
|
76
77
|
ArticlesHero: () => ArticlesHero,
|
|
@@ -110,37 +111,26 @@ function ArticleCategoryGrid({ categories, pageSize = 8 }) {
|
|
|
110
111
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-3xl font-bold text-foreground mb-4", children: "Browse by Category" }),
|
|
111
112
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-lg text-muted-foreground", children: "Explore our articles by topic to find the insights most relevant to your interests." })
|
|
112
113
|
] }),
|
|
113
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
114
|
-
"div",
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-4 text-center", children: [
|
|
134
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "font-semibold text-foreground text-base", children: cat.name }),
|
|
135
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
|
|
136
|
-
cat.count,
|
|
137
|
-
" ",
|
|
138
|
-
cat.count === 1 ? "article" : "articles"
|
|
139
|
-
] })
|
|
140
|
-
] })
|
|
141
|
-
] }) }, cat.slug))
|
|
142
|
-
}
|
|
143
|
-
),
|
|
114
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-6", children: visible.map((cat) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: `/articles/category/${cat.slug}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "group overflow-hidden rounded-xl border border-border shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer bg-card", children: [
|
|
115
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-hidden", style: { position: "relative", height: "13rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
116
|
+
import_image.default,
|
|
117
|
+
{
|
|
118
|
+
src: cat.featuredImage,
|
|
119
|
+
alt: cat.name,
|
|
120
|
+
fill: true,
|
|
121
|
+
sizes: "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw",
|
|
122
|
+
className: "object-cover group-hover:scale-105 transition-transform duration-300"
|
|
123
|
+
}
|
|
124
|
+
) }),
|
|
125
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-4 text-center", children: [
|
|
126
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "font-semibold text-foreground text-base", children: cat.name }),
|
|
127
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-sm text-muted-foreground mt-1", children: [
|
|
128
|
+
cat.count,
|
|
129
|
+
" ",
|
|
130
|
+
cat.count === 1 ? "article" : "articles"
|
|
131
|
+
] })
|
|
132
|
+
] })
|
|
133
|
+
] }) }, cat.slug)) }),
|
|
144
134
|
hasMore && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-10 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
145
135
|
"button",
|
|
146
136
|
{
|
|
@@ -670,10 +660,110 @@ function ArticlesPage({ config }) {
|
|
|
670
660
|
] });
|
|
671
661
|
}
|
|
672
662
|
|
|
673
|
-
// src/
|
|
663
|
+
// src/ArticleDetailHero.tsx
|
|
674
664
|
var import_image4 = __toESM(require("next/image"), 1);
|
|
675
665
|
var import_link5 = __toESM(require("next/link"), 1);
|
|
666
|
+
var import_lucide_react5 = require("lucide-react");
|
|
676
667
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
668
|
+
function toSlug(cat) {
|
|
669
|
+
return cat.toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-]/g, "");
|
|
670
|
+
}
|
|
671
|
+
function ArticleDetailHero({ article, categoryBasePath }) {
|
|
672
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
673
|
+
"section",
|
|
674
|
+
{
|
|
675
|
+
style: {
|
|
676
|
+
position: "relative",
|
|
677
|
+
height: "18rem",
|
|
678
|
+
display: "flex",
|
|
679
|
+
alignItems: "center",
|
|
680
|
+
justifyContent: "center",
|
|
681
|
+
overflow: "hidden"
|
|
682
|
+
},
|
|
683
|
+
children: [
|
|
684
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
685
|
+
import_image4.default,
|
|
686
|
+
{
|
|
687
|
+
src: article.featuredImage,
|
|
688
|
+
alt: article.title,
|
|
689
|
+
fill: true,
|
|
690
|
+
sizes: "100vw",
|
|
691
|
+
className: "object-cover",
|
|
692
|
+
priority: true
|
|
693
|
+
}
|
|
694
|
+
),
|
|
695
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
696
|
+
"div",
|
|
697
|
+
{
|
|
698
|
+
style: {
|
|
699
|
+
position: "absolute",
|
|
700
|
+
inset: 0,
|
|
701
|
+
backgroundColor: "rgba(0,0,0,0.6)"
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
),
|
|
705
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
706
|
+
"div",
|
|
707
|
+
{
|
|
708
|
+
style: {
|
|
709
|
+
position: "relative",
|
|
710
|
+
zIndex: 10,
|
|
711
|
+
textAlign: "center",
|
|
712
|
+
color: "white",
|
|
713
|
+
padding: "0 1rem",
|
|
714
|
+
maxWidth: "56rem",
|
|
715
|
+
margin: "0 auto",
|
|
716
|
+
width: "100%"
|
|
717
|
+
},
|
|
718
|
+
children: [
|
|
719
|
+
article.categories && article.categories.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { display: "flex", flexWrap: "wrap", justifyContent: "center", gap: "0.5rem", marginBottom: "1rem" }, children: article.categories.slice(0, 4).map(
|
|
720
|
+
(cat) => categoryBasePath ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
721
|
+
import_link5.default,
|
|
722
|
+
{
|
|
723
|
+
href: `${categoryBasePath}/${toSlug(cat)}`,
|
|
724
|
+
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full hover:bg-white/30 transition-colors",
|
|
725
|
+
children: cat
|
|
726
|
+
},
|
|
727
|
+
cat
|
|
728
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
729
|
+
"span",
|
|
730
|
+
{
|
|
731
|
+
className: "inline-block bg-white/20 text-white text-sm font-medium px-3 py-1 rounded-full",
|
|
732
|
+
children: cat
|
|
733
|
+
},
|
|
734
|
+
cat
|
|
735
|
+
)
|
|
736
|
+
) }),
|
|
737
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-4", children: article.title }),
|
|
738
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "1.5rem", fontSize: "0.875rem", color: "rgba(255,255,255,0.8)" }, children: [
|
|
739
|
+
article.date && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
740
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.Calendar, { className: "h-4 w-4" }),
|
|
741
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: article.date })
|
|
742
|
+
] }),
|
|
743
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
744
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.Clock, { className: "h-4 w-4" }),
|
|
745
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: article.readTime })
|
|
746
|
+
] }),
|
|
747
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
748
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.User, { className: "h-4 w-4" }),
|
|
749
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
|
|
750
|
+
"By ",
|
|
751
|
+
article.author
|
|
752
|
+
] })
|
|
753
|
+
] })
|
|
754
|
+
] })
|
|
755
|
+
]
|
|
756
|
+
}
|
|
757
|
+
)
|
|
758
|
+
]
|
|
759
|
+
}
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/CategoryArticlesPage.tsx
|
|
764
|
+
var import_image5 = __toESM(require("next/image"), 1);
|
|
765
|
+
var import_link6 = __toESM(require("next/link"), 1);
|
|
766
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
677
767
|
function getCategoryDescription(config, slug, categoryName) {
|
|
678
768
|
var _a;
|
|
679
769
|
const raw = (_a = config.categoryDescriptions) == null ? void 0 : _a[slug];
|
|
@@ -689,8 +779,8 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
689
779
|
const description = getCategoryDescription(config, category, categoryName);
|
|
690
780
|
const pageSize = (_a = config.pageSize) != null ? _a : DEFAULT_PAGE_SIZE;
|
|
691
781
|
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
692
|
-
return /* @__PURE__ */ (0,
|
|
693
|
-
/* @__PURE__ */ (0,
|
|
782
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
783
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
694
784
|
BreadcrumbSchema,
|
|
695
785
|
{
|
|
696
786
|
items: [
|
|
@@ -700,9 +790,9 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
700
790
|
]
|
|
701
791
|
}
|
|
702
792
|
),
|
|
703
|
-
/* @__PURE__ */ (0,
|
|
704
|
-
/* @__PURE__ */ (0,
|
|
705
|
-
|
|
793
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "relative h-72 md:h-96 flex items-center justify-center overflow-hidden", children: [
|
|
794
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
795
|
+
import_image5.default,
|
|
706
796
|
{
|
|
707
797
|
src: heroImage,
|
|
708
798
|
alt: categoryName,
|
|
@@ -712,24 +802,24 @@ function CategoryArticlesPage({ category, articles, config }) {
|
|
|
712
802
|
priority: true
|
|
713
803
|
}
|
|
714
804
|
),
|
|
715
|
-
/* @__PURE__ */ (0,
|
|
716
|
-
/* @__PURE__ */ (0,
|
|
717
|
-
/* @__PURE__ */ (0,
|
|
718
|
-
/* @__PURE__ */ (0,
|
|
719
|
-
description.long && /* @__PURE__ */ (0,
|
|
720
|
-
/* @__PURE__ */ (0,
|
|
805
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "absolute inset-0 bg-black/60" }),
|
|
806
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "relative z-10 text-center text-white px-4", children: [
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm font-semibold uppercase tracking-widest mb-3 opacity-80", children: "Category" }),
|
|
808
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { className: "text-4xl md:text-5xl font-bold mb-3", children: categoryName }),
|
|
809
|
+
description.long && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-lg opacity-90 max-w-2xl mx-auto mt-2", children: description.long }),
|
|
810
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-lg opacity-70 mt-2", children: [
|
|
721
811
|
articles.length,
|
|
722
812
|
" article",
|
|
723
813
|
articles.length === 1 ? "" : "s"
|
|
724
814
|
] })
|
|
725
815
|
] })
|
|
726
816
|
] }),
|
|
727
|
-
/* @__PURE__ */ (0,
|
|
728
|
-
/* @__PURE__ */ (0,
|
|
729
|
-
/* @__PURE__ */ (0,
|
|
730
|
-
/* @__PURE__ */ (0,
|
|
817
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("section", { className: "py-16 bg-muted/50 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
818
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mb-8 flex items-center justify-between", children: [
|
|
819
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_link6.default, { href: "/articles", className: "text-sm text-primary hover:underline", children: "\u2190 All Articles" }),
|
|
820
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-muted-foreground", children: description.short })
|
|
731
821
|
] }),
|
|
732
|
-
/* @__PURE__ */ (0,
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(LatestArticles, { articles, pageSize })
|
|
733
823
|
] }) })
|
|
734
824
|
] });
|
|
735
825
|
}
|
|
@@ -740,7 +830,7 @@ var import_react7 = require("react");
|
|
|
740
830
|
|
|
741
831
|
// src/CommentForm.tsx
|
|
742
832
|
var import_react4 = require("react");
|
|
743
|
-
var
|
|
833
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
744
834
|
var MAX_LENGTH = 2e3;
|
|
745
835
|
var WARN_THRESHOLD = 1800;
|
|
746
836
|
function submitLabel(submitting, parentId) {
|
|
@@ -779,8 +869,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
779
869
|
}
|
|
780
870
|
});
|
|
781
871
|
}
|
|
782
|
-
return /* @__PURE__ */ (0,
|
|
783
|
-
/* @__PURE__ */ (0,
|
|
872
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
|
|
873
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
784
874
|
"textarea",
|
|
785
875
|
{
|
|
786
876
|
value: body,
|
|
@@ -793,13 +883,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
793
883
|
"aria-label": parentId ? "Reply text" : "Comment text"
|
|
794
884
|
}
|
|
795
885
|
),
|
|
796
|
-
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0,
|
|
886
|
+
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
797
887
|
remaining,
|
|
798
888
|
" characters remaining"
|
|
799
889
|
] }),
|
|
800
|
-
error && /* @__PURE__ */ (0,
|
|
801
|
-
/* @__PURE__ */ (0,
|
|
802
|
-
/* @__PURE__ */ (0,
|
|
890
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-xs text-destructive", children: error }),
|
|
891
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-2", children: [
|
|
892
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
803
893
|
"button",
|
|
804
894
|
{
|
|
805
895
|
type: "submit",
|
|
@@ -808,7 +898,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
808
898
|
children: submitLabel(submitting, parentId)
|
|
809
899
|
}
|
|
810
900
|
),
|
|
811
|
-
onCancel && /* @__PURE__ */ (0,
|
|
901
|
+
onCancel && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
812
902
|
"button",
|
|
813
903
|
{
|
|
814
904
|
type: "button",
|
|
@@ -823,7 +913,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
823
913
|
|
|
824
914
|
// src/CommentItem.tsx
|
|
825
915
|
var import_react5 = require("react");
|
|
826
|
-
var
|
|
916
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
827
917
|
function relativeTime(iso) {
|
|
828
918
|
const diff = Date.now() - new Date(iso).getTime();
|
|
829
919
|
const minutes = Math.floor(diff / 6e4);
|
|
@@ -861,15 +951,15 @@ function CommentItem({
|
|
|
861
951
|
}
|
|
862
952
|
});
|
|
863
953
|
}
|
|
864
|
-
return /* @__PURE__ */ (0,
|
|
865
|
-
/* @__PURE__ */ (0,
|
|
866
|
-
/* @__PURE__ */ (0,
|
|
867
|
-
/* @__PURE__ */ (0,
|
|
868
|
-
/* @__PURE__ */ (0,
|
|
954
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
|
|
955
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
957
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
|
|
958
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
|
|
869
959
|
] }),
|
|
870
|
-
/* @__PURE__ */ (0,
|
|
871
|
-
/* @__PURE__ */ (0,
|
|
872
|
-
canReply && /* @__PURE__ */ (0,
|
|
960
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex gap-3 pt-1", children: [
|
|
962
|
+
canReply && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
873
963
|
"button",
|
|
874
964
|
{
|
|
875
965
|
onClick: () => setShowReplyForm((v) => !v),
|
|
@@ -877,7 +967,7 @@ function CommentItem({
|
|
|
877
967
|
children: showReplyForm ? "Cancel" : "Reply"
|
|
878
968
|
}
|
|
879
969
|
),
|
|
880
|
-
canDelete && /* @__PURE__ */ (0,
|
|
970
|
+
canDelete && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
881
971
|
"button",
|
|
882
972
|
{
|
|
883
973
|
onClick: handleDelete,
|
|
@@ -888,7 +978,7 @@ function CommentItem({
|
|
|
888
978
|
)
|
|
889
979
|
] })
|
|
890
980
|
] }) }),
|
|
891
|
-
showReplyForm && /* @__PURE__ */ (0,
|
|
981
|
+
showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
892
982
|
CommentForm,
|
|
893
983
|
{
|
|
894
984
|
articleSlug,
|
|
@@ -900,7 +990,7 @@ function CommentItem({
|
|
|
900
990
|
onCancel: () => setShowReplyForm(false)
|
|
901
991
|
}
|
|
902
992
|
) }),
|
|
903
|
-
replies.length > 0 && /* @__PURE__ */ (0,
|
|
993
|
+
replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
904
994
|
CommentItem,
|
|
905
995
|
{
|
|
906
996
|
comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
|
|
@@ -915,7 +1005,7 @@ function CommentItem({
|
|
|
915
1005
|
}
|
|
916
1006
|
|
|
917
1007
|
// src/CommentThread.tsx
|
|
918
|
-
var
|
|
1008
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
919
1009
|
function CommentThread({
|
|
920
1010
|
comments,
|
|
921
1011
|
articleSlug,
|
|
@@ -923,9 +1013,9 @@ function CommentThread({
|
|
|
923
1013
|
onRefresh
|
|
924
1014
|
}) {
|
|
925
1015
|
if (comments.length === 0) {
|
|
926
|
-
return /* @__PURE__ */ (0,
|
|
1016
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
|
|
927
1017
|
}
|
|
928
|
-
return /* @__PURE__ */ (0,
|
|
1018
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
929
1019
|
CommentItem,
|
|
930
1020
|
{
|
|
931
1021
|
comment,
|
|
@@ -938,7 +1028,7 @@ function CommentThread({
|
|
|
938
1028
|
}
|
|
939
1029
|
|
|
940
1030
|
// src/CommentsSection.tsx
|
|
941
|
-
var
|
|
1031
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
942
1032
|
function CommentsSection({ articleSlug, config }) {
|
|
943
1033
|
var _a, _b, _c, _d, _e, _f;
|
|
944
1034
|
const { data: session, status } = (0, import_react6.useSession)();
|
|
@@ -970,10 +1060,10 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
970
1060
|
if (!enabled) return null;
|
|
971
1061
|
const count = comments.length;
|
|
972
1062
|
const label = count === 1 ? "1 comment" : `${count} comments`;
|
|
973
|
-
return /* @__PURE__ */ (0,
|
|
974
|
-
/* @__PURE__ */ (0,
|
|
975
|
-
status === "authenticated" ? /* @__PURE__ */ (0,
|
|
976
|
-
/* @__PURE__ */ (0,
|
|
1063
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
|
|
1064
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
|
|
1065
|
+
status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
977
1067
|
"button",
|
|
978
1068
|
{
|
|
979
1069
|
onClick: () => (0, import_react6.signIn)(),
|
|
@@ -984,8 +1074,8 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
984
1074
|
" ",
|
|
985
1075
|
"to join the discussion."
|
|
986
1076
|
] }),
|
|
987
|
-
fetchError && /* @__PURE__ */ (0,
|
|
988
|
-
loading ? /* @__PURE__ */ (0,
|
|
1077
|
+
fetchError && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
|
|
1078
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
989
1079
|
CommentThread,
|
|
990
1080
|
{
|
|
991
1081
|
comments,
|
|
@@ -1000,6 +1090,7 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
1000
1090
|
0 && (module.exports = {
|
|
1001
1091
|
ArticleCard,
|
|
1002
1092
|
ArticleCategoryGrid,
|
|
1093
|
+
ArticleDetailHero,
|
|
1003
1094
|
ArticleSchema,
|
|
1004
1095
|
ArticleSearchBar,
|
|
1005
1096
|
ArticlesHero,
|