@opinly/next 0.1.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.
Files changed (35) hide show
  1. package/.turbo/turbo-lint.log +14 -0
  2. package/CHANGELOG.md +21 -0
  3. package/dist/index.cjs +876 -0
  4. package/dist/index.d.ts +17 -0
  5. package/dist/index.js +838 -0
  6. package/dist/index.mjs +838 -0
  7. package/package.json +41 -0
  8. package/src/components/author-page/_helpers/json-ld.ts +0 -0
  9. package/src/components/author-page/index.tsx +62 -0
  10. package/src/components/authors-page/index.tsx +90 -0
  11. package/src/components/blog-post/_components/content.tsx +141 -0
  12. package/src/components/blog-post/_components/faq.tsx +18 -0
  13. package/src/components/blog-post/_components/share.tsx +48 -0
  14. package/src/components/blog-post/_components/table-of-contents.tsx +63 -0
  15. package/src/components/blog-post/_helpers/calc-reading-time.ts +0 -0
  16. package/src/components/blog-post/_helpers/extract-headings.ts +25 -0
  17. package/src/components/blog-post/_helpers/json-ld.ts +176 -0
  18. package/src/components/blog-post/_helpers/reading-time.ts +28 -0
  19. package/src/components/blog-post/_types/blog-post.ts +6 -0
  20. package/src/components/blog-post/_types/class-names.ts +10 -0
  21. package/src/components/blog-post/index.tsx +126 -0
  22. package/src/components/bread-crumbs.tsx +48 -0
  23. package/src/components/folder-page/index.tsx +50 -0
  24. package/src/components/home-page.tsx +65 -0
  25. package/src/components/not-found.tsx +36 -0
  26. package/src/components/opinly-blog.tsx +37 -0
  27. package/src/components/post-preview.tsx +73 -0
  28. package/src/index.ts +2 -0
  29. package/src/sdk-config.ts +37 -0
  30. package/src/utils/dates.ts +6 -0
  31. package/src/utils/generate-metadata.ts +54 -0
  32. package/src/utils/images.ts +7 -0
  33. package/src/utils/merge.ts +6 -0
  34. package/sst-env.d.ts +9 -0
  35. package/tsconfig.json +19 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,876 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ HomePage: () => HomePage,
34
+ OpinlyBlog: () => OpinlyBlog
35
+ });
36
+ module.exports = __toCommonJS(src_exports);
37
+
38
+ // src/components/blog-post/index.tsx
39
+ var import_link5 = __toESM(require("next/link"), 1);
40
+
41
+ // src/utils/merge.ts
42
+ var import_clsx = require("clsx");
43
+ var import_tailwind_merge = require("tailwind-merge");
44
+ var cn = (...inputs) => {
45
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
46
+ };
47
+
48
+ // src/sdk-config.ts
49
+ var siteUrl = process.env.OPINLY_SITE_URL;
50
+ var opinlyEnvVars = {
51
+ OPINLY_IMAGES_PREFIX: process.env.OPINLY_IMAGES_PREFIX,
52
+ OPINLY_SITE_URL: process.env.OPINLY_SITE_URL,
53
+ OPINLY_BLOG_PREFIX: process.env.OPINLY_BLOG_PREFIX,
54
+ OPINLY_CDN_URL: process.env.OPINLY_CDN_URL,
55
+ OPINLY_SITE_NAME: process.env.OPINLY_SITE_NAME
56
+ };
57
+ Object.entries(opinlyEnvVars).forEach(([key, value]) => {
58
+ if (!value) {
59
+ throw new Error(`OPINLY ERROR: env var ${key} is not set`);
60
+ }
61
+ });
62
+ var opinlyConfig = {
63
+ cdnUrl: process.env.OPINLY_CDN_URL || "https://cdn.opinly.ai",
64
+ siteUrl: process.env.OPINLY_SITE_URL,
65
+ imagesPrefix: process.env.OPINLY_IMAGES_PREFIX,
66
+ imagesUrl: `${process.env.OPINLY_SITE_URL}${process.env.OPINLY_IMAGES_PREFIX}`,
67
+ blogPrefix: process.env.OPINLY_BLOG_PREFIX,
68
+ blogUrl: `${process.env.OPINLY_SITE_URL}${process.env.OPINLY_BLOG_PREFIX}`,
69
+ siteName: process.env.OPINLY_SITE_NAME
70
+ };
71
+
72
+ // src/utils/images.ts
73
+ function replaceImageNameSpace(fileKey) {
74
+ const pathParts = fileKey.split("/").filter(Boolean);
75
+ if (pathParts.length > 1) {
76
+ pathParts.shift();
77
+ }
78
+ return pathParts.join("/");
79
+ }
80
+
81
+ // src/components/blog-post/_helpers/json-ld.ts
82
+ function generateArticleBodyHtml(content) {
83
+ if (!content?.content)
84
+ return "";
85
+ let htmlString = "";
86
+ content.content.forEach((node) => {
87
+ switch (node.type) {
88
+ case "paragraph":
89
+ htmlString += `<p>${generateChildrenHtml(node.content)}</p>`;
90
+ break;
91
+ case "heading":
92
+ const headingLevel = node.attrs?.level || 1;
93
+ htmlString += `<h${headingLevel}>${generateChildrenHtml(node.content)}</h${headingLevel}>`;
94
+ break;
95
+ case "bulletList":
96
+ htmlString += `<ul>${generateChildrenHtml(node.content)}</ul>`;
97
+ break;
98
+ case "orderedList":
99
+ htmlString += `<ol>${generateChildrenHtml(node.content)}</ol>`;
100
+ break;
101
+ case "listItem":
102
+ htmlString += `<li>${generateChildrenHtml(node.content)}</li>`;
103
+ break;
104
+ default:
105
+ break;
106
+ }
107
+ });
108
+ return htmlString;
109
+ }
110
+ function generateChildrenHtml(nodes) {
111
+ if (!nodes)
112
+ return "";
113
+ let childrenHtml = "";
114
+ nodes.forEach((child) => {
115
+ if (child.type === "text") {
116
+ let text = child.text || "";
117
+ if (child.marks) {
118
+ child.marks.forEach((mark) => {
119
+ if (mark.type === "bold")
120
+ text = `<strong>${text}</strong>`;
121
+ if (mark.type === "italic")
122
+ text = `<em>${text}</em>`;
123
+ if (mark.type === "link" && mark.attrs?.href) {
124
+ text = `<a href="${mark.attrs.href}">${text}</a>`;
125
+ }
126
+ });
127
+ }
128
+ childrenHtml += text;
129
+ }
130
+ });
131
+ return childrenHtml;
132
+ }
133
+ var genFaqJSONLD = (faqs) => {
134
+ return {
135
+ "@context": "https://schema.org",
136
+ "@type": "FAQPage",
137
+ mainEntity: faqs.map((faq) => ({
138
+ "@type": "Question",
139
+ name: faq.question,
140
+ acceptedAnswer: {
141
+ "@type": "Answer",
142
+ text: faq.answer
143
+ }
144
+ }))
145
+ };
146
+ };
147
+ var genBlogPostingJSONLD = (post) => {
148
+ return {
149
+ "@context": "https://schema.org",
150
+ "@type": "BlogPosting",
151
+ headline: post.title,
152
+ description: post.description || void 0,
153
+ url: `${opinlyConfig.blogUrl}/${post.folder ? `${post.folder.slug + "/"}${post.slug}` : post.slug}`,
154
+ datePublished: post.firstPublishedAt || void 0,
155
+ dateModified: post.modifiedAt || void 0,
156
+ image: post.titleFile?.fileKey ? {
157
+ "@type": "ImageObject",
158
+ url: `${opinlyConfig.imagesUrl}/${replaceImageNameSpace(post.titleFile.fileKey)}`
159
+ } : void 0,
160
+ author: post.author ? {
161
+ "@type": "Person",
162
+ name: post.author.name,
163
+ url: `${opinlyConfig.blogUrl}/author/${post.author.slug}`,
164
+ description: post.author.bio || void 0
165
+ } : void 0,
166
+ isAccessibleForFree: true,
167
+ articleBody: generateArticleBodyHtml(post.content)
168
+ };
169
+ };
170
+ var genBreadcrumbJSONLD = (post) => {
171
+ const itemListElement = [
172
+ {
173
+ "@type": "ListItem",
174
+ position: 1,
175
+ name: "Company Name",
176
+ item: opinlyConfig.siteUrl
177
+ },
178
+ {
179
+ "@type": "ListItem",
180
+ position: 2,
181
+ name: post.folder?.name || "Blog",
182
+ item: `${opinlyConfig.blogUrl}/blog`
183
+ }
184
+ ];
185
+ if (post.folder) {
186
+ itemListElement.push({
187
+ "@type": "ListItem",
188
+ position: 3,
189
+ name: post.folder.name,
190
+ item: `${opinlyConfig.blogUrl}/blog/${post.folder.slug}`
191
+ });
192
+ itemListElement.push({
193
+ "@type": "ListItem",
194
+ position: 4,
195
+ name: post.title,
196
+ item: `${opinlyConfig.blogUrl}/blog/${post.folder.slug}/${post.slug}`
197
+ });
198
+ } else {
199
+ itemListElement.push({
200
+ "@type": "ListItem",
201
+ position: 3,
202
+ name: post.title,
203
+ item: `${opinlyConfig.blogUrl}/blog/${post.slug}`
204
+ });
205
+ }
206
+ return {
207
+ "@context": "https://schema.org",
208
+ "@type": "BreadcrumbList",
209
+ itemListElement
210
+ };
211
+ };
212
+
213
+ // src/components/blog-post/_components/faq.tsx
214
+ var import_jsx_runtime = require("react/jsx-runtime");
215
+ var FAQS = ({ faqs }) => {
216
+ if (!faqs)
217
+ return null;
218
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-lg font-semibold mb-2", children: "FAQS" }),
220
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: faqs.map((faq) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-lg font-semibold mb-2", children: faq.question }),
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-gray-700", children: faq.answer })
223
+ ] }, faq.question)) })
224
+ ] });
225
+ };
226
+
227
+ // src/components/blog-post/_helpers/reading-time.ts
228
+ var countWords = (content) => {
229
+ if (!content?.content)
230
+ return 0;
231
+ let wordCount = 0;
232
+ const countWords2 = (nodes) => {
233
+ for (const node of nodes) {
234
+ if (node.type === "text" && node.text) {
235
+ const words = node.text.trim().split(/\s+/).filter(Boolean);
236
+ wordCount += words.length;
237
+ }
238
+ if (node.content)
239
+ countWords2(node.content);
240
+ }
241
+ };
242
+ countWords2(content.content);
243
+ return wordCount;
244
+ };
245
+ function calculateReadingTime(content) {
246
+ const wordCount = countWords(content.content);
247
+ const readingTimeMinutes = Math.ceil(wordCount / 200);
248
+ return readingTimeMinutes;
249
+ }
250
+
251
+ // src/components/blog-post/_components/content.tsx
252
+ var import_link = __toESM(require("next/link"), 1);
253
+ var import_jsx_runtime2 = require("react/jsx-runtime");
254
+ function renderTiptapContent(content, classNames) {
255
+ if (!content?.content)
256
+ return [];
257
+ return content.content.map((node, index) => {
258
+ switch (node.type) {
259
+ case "paragraph":
260
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: classNames.paragraph || "mb-4", children: renderChildren(node.content, classNames) }, index);
261
+ case "heading": {
262
+ const text = node.content?.[0]?.text ?? "";
263
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
264
+ const Tag = `h${node.attrs?.level || 1}`;
265
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
266
+ Tag,
267
+ {
268
+ id: slug,
269
+ className: classNames.heading || "mt-8 mb-4 font-bold text-gray-800",
270
+ children: renderChildren(node.content, classNames)
271
+ },
272
+ index
273
+ );
274
+ }
275
+ case "image":
276
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
277
+ "div",
278
+ {
279
+ className: cn(
280
+ "relative w-full max-w-[800px] my-6",
281
+ classNames.image
282
+ ),
283
+ style: { aspectRatio: "2 / 1" }
284
+ },
285
+ index
286
+ );
287
+ case "bulletList":
288
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
289
+ "ul",
290
+ {
291
+ className: classNames.bulletList || "list-disc pl-6 my-4",
292
+ children: renderChildren(node.content, classNames)
293
+ },
294
+ index
295
+ );
296
+ case "orderedList":
297
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
298
+ "ol",
299
+ {
300
+ className: classNames.orderedList || "list-decimal pl-6 my-4",
301
+ children: renderChildren(node.content, classNames)
302
+ },
303
+ index
304
+ );
305
+ case "listItem":
306
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { className: classNames.listItem || "my-1", children: renderChildren(node.content, classNames) }, index);
307
+ default:
308
+ return null;
309
+ }
310
+ }).filter(Boolean);
311
+ }
312
+ var Content = ({
313
+ content,
314
+ classNames
315
+ }) => {
316
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "prose min-w-full", children: renderTiptapContent(content, classNames) });
317
+ };
318
+ function renderChildren(nodes, classNames) {
319
+ if (!nodes)
320
+ return [];
321
+ return nodes.map((child, idx) => {
322
+ if (child.type === "text") {
323
+ let text = child.text;
324
+ if (child.marks) {
325
+ child.marks.forEach((mark) => {
326
+ if (mark.type === "bold")
327
+ text = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: text }, idx);
328
+ if (mark.type === "italic")
329
+ text = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("em", { children: text }, idx);
330
+ if (mark.type === "link")
331
+ text = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
332
+ import_link.default,
333
+ {
334
+ href: mark.attrs?.href,
335
+ className: classNames.link || "text-blue-600 underline",
336
+ children: text
337
+ },
338
+ idx
339
+ );
340
+ });
341
+ }
342
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: text }, idx);
343
+ }
344
+ return null;
345
+ }).filter(Boolean);
346
+ }
347
+
348
+ // src/components/blog-post/_components/table-of-contents.tsx
349
+ var import_image = __toESM(require("next/image"), 1);
350
+
351
+ // src/components/blog-post/_helpers/extract-headings.ts
352
+ function extractHeadings(content) {
353
+ const headings = [];
354
+ if (!content?.content)
355
+ return headings;
356
+ const walk = (nodes) => {
357
+ for (const node of nodes) {
358
+ if (node.type === "heading" && node.content?.[0]?.text) {
359
+ const text = node.content[0].text;
360
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
361
+ headings.push({ text, slug });
362
+ }
363
+ if (node.content)
364
+ walk(node.content);
365
+ }
366
+ };
367
+ walk(content.content);
368
+ return headings;
369
+ }
370
+
371
+ // src/components/blog-post/_components/share.tsx
372
+ var import_lucide_react = require("lucide-react");
373
+ var import_link2 = __toESM(require("next/link"), 1);
374
+ var import_jsx_runtime3 = require("react/jsx-runtime");
375
+ var Share = ({ post }) => {
376
+ const postUrl = encodeURIComponent(`${opinlyConfig.blogUrl}/${post.slug}`);
377
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex gap-2 mb-6", children: [
378
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
379
+ import_link2.default,
380
+ {
381
+ href: `https://linkedin.com/sharing/share-offsite?url=${postUrl}`,
382
+ target: "_blank",
383
+ rel: "noopener noreferrer",
384
+ className: "border p-1 rounded flex items-center justify-center w-8 h-8",
385
+ children: [
386
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "sr-only", children: "Share on LinkedIn" }),
387
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Linkedin, { className: "w-4 h-4" })
388
+ ]
389
+ }
390
+ ),
391
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
392
+ import_link2.default,
393
+ {
394
+ href: `https://twitter.com/intent/tweet?url=${postUrl}&text=${encodeURIComponent(
395
+ `Check out this article by ${post.author?.name}`
396
+ )}`,
397
+ target: "_blank",
398
+ rel: "noopener noreferrer",
399
+ className: "border p-1 rounded flex items-center justify-center w-8 h-8",
400
+ children: [
401
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "sr-only", children: "Share on Twitter" }),
402
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Twitter, {})
403
+ ]
404
+ }
405
+ ),
406
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
407
+ import_link2.default,
408
+ {
409
+ href: `https://facebook.com/sharer/sharer.php?u=${postUrl}`,
410
+ target: "_blank",
411
+ rel: "noopener noreferrer",
412
+ className: "border p-1 rounded flex items-center justify-center w-8 h-8",
413
+ children: [
414
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "sr-only", children: "Share on Facebook" }),
415
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Facebook, {})
416
+ ]
417
+ }
418
+ ),
419
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
420
+ import_link2.default,
421
+ {
422
+ href: `mailto:?body=${encodeURIComponent(`${post.title}
423
+ ${postUrl}`)}`,
424
+ className: "border p-1 rounded flex items-center justify-center w-8 h-8",
425
+ children: [
426
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "sr-only", children: "Share via Email" }),
427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react.Mail, { className: "w-4 h-4" })
428
+ ]
429
+ }
430
+ )
431
+ ] });
432
+ };
433
+
434
+ // src/components/blog-post/_components/table-of-contents.tsx
435
+ var import_link3 = __toESM(require("next/link"), 1);
436
+ var import_jsx_runtime4 = require("react/jsx-runtime");
437
+ var TableOfContents = ({
438
+ content,
439
+ author,
440
+ post
441
+ }) => {
442
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("aside", { className: cn("w-full lg:w-1/3 lg:sticky lg:top-20 lg:self-start"), children: [
443
+ author && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "w-full mb-6 border p-4 rounded-md text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-3 mb-3", children: [
444
+ author.fileKey && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
445
+ import_image.default,
446
+ {
447
+ src: `${opinlyConfig.imagesPrefix}/${replaceImageNameSpace(author.fileKey)}`,
448
+ alt: author.name,
449
+ width: 48,
450
+ height: 48,
451
+ className: "rounded-full"
452
+ }
453
+ ),
454
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
455
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
456
+ import_link3.default,
457
+ {
458
+ href: `${opinlyConfig.blogPrefix}/authors/${author.slug}`,
459
+ className: "text-xs uppercase text-muted-foreground font-medium",
460
+ children: author.name
461
+ }
462
+ ),
463
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-muted-foreground text-sm", children: author.bio })
464
+ ] })
465
+ ] }) }),
466
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Share, { post }),
467
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "border-l-4 border-opinlyAccent pl-4 mb-4", children: [
468
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "text-lg font-semibold mb-2", children: "TABLE OF CONTENTS" }),
469
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "text-sm text-gray-700 space-y-1", children: (extractHeadings(content) || []).map((heading) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: `#${heading.slug}`, className: "hover:underline", children: heading.text }) }, heading.slug)) })
470
+ ] })
471
+ ] });
472
+ };
473
+
474
+ // src/components/bread-crumbs.tsx
475
+ var import_link4 = __toESM(require("next/link"), 1);
476
+ var import_react = require("react");
477
+ var import_jsx_runtime5 = require("react/jsx-runtime");
478
+ var BreadCrumbs = ({
479
+ breadcrumbs
480
+ }) => {
481
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("nav", { className: "text-sm text-orange-100 text-muted-foreground mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("ol", { className: "list-reset flex space-x-2", children: [
482
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_link4.default, { href: "/", className: "font-bold", children: opinlyConfig.siteName }),
483
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "/" }),
484
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_link4.default, { href: opinlyConfig.blogPrefix, className: "font-bold", children: "Blog" }),
485
+ breadcrumbs?.map((breadcrumb, index) => {
486
+ if (!breadcrumb) {
487
+ return null;
488
+ }
489
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react.Fragment, { children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "/" }),
491
+ breadcrumb.url ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_link4.default, { href: breadcrumb.url, className: "font-bold", children: breadcrumb.name }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "font-bold", children: breadcrumb.name })
492
+ ] }, index);
493
+ })
494
+ ] }) });
495
+ };
496
+
497
+ // src/utils/dates.ts
498
+ var formatDate = (iso) => new Date(iso).toLocaleDateString("en-US", {
499
+ month: "short",
500
+ day: "2-digit",
501
+ year: "numeric"
502
+ });
503
+
504
+ // src/components/blog-post/index.tsx
505
+ var import_script = __toESM(require("next/script"), 1);
506
+ var import_jsx_runtime6 = require("react/jsx-runtime");
507
+ var BlogPost = ({
508
+ post,
509
+ classNames = {}
510
+ }) => {
511
+ const content = post.content;
512
+ console.log("content", content);
513
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
514
+ post.faqs && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
515
+ import_script.default,
516
+ {
517
+ strategy: "beforeInteractive",
518
+ type: "application/ld+json",
519
+ dangerouslySetInnerHTML: {
520
+ __html: JSON.stringify(genFaqJSONLD(post.faqs)).replace(
521
+ /</g,
522
+ "\\u003c"
523
+ )
524
+ }
525
+ }
526
+ ),
527
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
528
+ import_script.default,
529
+ {
530
+ strategy: "beforeInteractive",
531
+ type: "application/ld+json",
532
+ dangerouslySetInnerHTML: {
533
+ __html: JSON.stringify(genBlogPostingJSONLD(post))
534
+ }
535
+ }
536
+ ),
537
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
538
+ import_script.default,
539
+ {
540
+ strategy: "beforeInteractive",
541
+ type: "application/ld+json",
542
+ dangerouslySetInnerHTML: {
543
+ __html: JSON.stringify(genBreadcrumbJSONLD(post))
544
+ }
545
+ }
546
+ ),
547
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "max-w-7xl mx-auto px-4 py-24 flex items-start space-x-8", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-12 w-full", children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "bg-orange-600", children: [
549
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
550
+ BreadCrumbs,
551
+ {
552
+ breadcrumbs: [
553
+ post.folder && {
554
+ url: `${opinlyConfig.blogPrefix}/${post.folder.slug}`,
555
+ name: post.folder.name
556
+ }
557
+ ]
558
+ }
559
+ ),
560
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { className: "text-4xl font-bold mb-2 leading-tight", children: post.title }),
561
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-sm mb-4", children: [
562
+ "Author:",
563
+ " ",
564
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
565
+ import_link5.default,
566
+ {
567
+ href: `${opinlyConfig.blogPrefix}/authors/${post.author?.slug}`,
568
+ className: "font-medium",
569
+ children: post.author?.name
570
+ }
571
+ ),
572
+ " ",
573
+ "\xB7 ",
574
+ calculateReadingTime(content),
575
+ " min read \xB7",
576
+ " ",
577
+ formatDate(post.firstPublishedAt) || "Today"
578
+ ] })
579
+ ] }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
581
+ "div",
582
+ {
583
+ className: cn(
584
+ "flex flex-col-reverse md:flex-row mx-auto mt-10 gap-12 relative w-full ",
585
+ classNames.container
586
+ ),
587
+ children: [
588
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("article", { className: "w-full lg:w-2/3 order-2 lg:order-1 flex items-start space-x-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "prose w-full", children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Content, { content, classNames }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FAQS, { faqs: post.faqs })
591
+ ] }) }),
592
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "block md:min-w-72 md:sticky md:top-20 md:self-start", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
593
+ TableOfContents,
594
+ {
595
+ content,
596
+ author: post.author,
597
+ post
598
+ }
599
+ ) })
600
+ ]
601
+ }
602
+ )
603
+ ] }) })
604
+ ] });
605
+ };
606
+
607
+ // src/components/post-preview.tsx
608
+ var import_link6 = __toESM(require("next/link"), 1);
609
+ var import_image2 = __toESM(require("next/image"), 1);
610
+ var import_jsx_runtime7 = require("react/jsx-runtime");
611
+ var PostPreview = ({
612
+ post
613
+ }) => {
614
+ const postSlug = `${opinlyConfig.blogPrefix}/${post.folder?.slug ? `${post.folder.slug}/` : ""}${post.slug}`;
615
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
616
+ post.image?.fileKey && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_link6.default, { href: postSlug, className: "group", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "relative w-full h-48 mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
617
+ import_image2.default,
618
+ {
619
+ src: `${opinlyConfig.imagesPrefix}/${replaceImageNameSpace(post.image.fileKey)}`,
620
+ alt: post.title,
621
+ fill: true,
622
+ className: "object-cover rounded-lg"
623
+ }
624
+ ) }) }, post.slug),
625
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "text-sm text-muted-foreground mb-1", children: [
626
+ post.folder?.name && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
627
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
628
+ import_link6.default,
629
+ {
630
+ href: `${opinlyConfig.blogPrefix}/${post.folder.slug}`,
631
+ className: "hover:underline",
632
+ children: post.folder.name
633
+ }
634
+ ),
635
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mx-1", children: "\xB7" })
636
+ ] }),
637
+ " ",
638
+ formatDate(post.firstPublishedAt)
639
+ ] }),
640
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_link6.default, { href: postSlug, className: "group", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { className: "text-lg font-semibold leading-snug group-hover:underline", children: post.title }) }, post.slug),
641
+ post.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-sm text-gray-600 mt-1 line-clamp-2", children: post.description }),
642
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "text-xs text-gray-500 mt-2", children: [
643
+ post.author && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
644
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
645
+ import_link6.default,
646
+ {
647
+ href: `${opinlyConfig.blogPrefix}/authors/${post.author.slug}`,
648
+ className: "font-medium hover:underline",
649
+ children: post.author?.name
650
+ }
651
+ ),
652
+ " ",
653
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mx-1", children: "\xB7" })
654
+ ] }),
655
+ " ",
656
+ formatDate(post.firstPublishedAt)
657
+ ] })
658
+ ] });
659
+ };
660
+
661
+ // src/components/folder-page/index.tsx
662
+ var import_jsx_runtime8 = require("react/jsx-runtime");
663
+ var FolderPage = ({
664
+ folder
665
+ }) => {
666
+ console.log("folder", JSON.stringify(folder, null, 2));
667
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "max-w-6xl mx-auto px-4 py-10", children: [
668
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(BreadCrumbs, {}),
669
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { className: "text-4xl font-bold mb-2", children: folder.name }),
670
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-muted-foreground max-w-2xl mb-8", children: folder.description || `Explore articles about ${folder.name}.` }),
671
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8", children: [
672
+ folder.posts.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-muted-foreground", children: "No posts added yet" }),
673
+ folder.posts.map((post) => {
674
+ console.log("post.image", post.image);
675
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PostPreview, { post }, post.slug);
676
+ })
677
+ ] })
678
+ ] });
679
+ };
680
+
681
+ // src/components/home-page.tsx
682
+ var import_link7 = __toESM(require("next/link"), 1);
683
+ var import_jsx_runtime9 = require("react/jsx-runtime");
684
+ var HomePage = ({
685
+ homePage,
686
+ page
687
+ }) => {
688
+ const { posts, folders, count } = homePage;
689
+ console.log("posts", JSON.stringify(posts, null, 2));
690
+ const currentPage = page;
691
+ const totalPages = Math.ceil(count / 10);
692
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "max-w-7xl mx-auto px-4 py-10", children: [
693
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-10", children: posts.map((post) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PostPreview, { post }, post.slug)) }),
694
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex justify-center items-center mt-10 gap-2", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
695
+ import_link7.default,
696
+ {
697
+ href: `?page=${p}`,
698
+ className: `px-3 py-1 border rounded ${p === currentPage ? "bg-black text-white" : "hover:bg-gray-100 text-gray-700"}`,
699
+ children: p
700
+ },
701
+ p
702
+ )) }),
703
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-wrap gap-2 mb-10", children: folders.map((folder) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
704
+ import_link7.default,
705
+ {
706
+ href: `/blog/${folder.slug}`,
707
+ className: "px-4 py-1 bg-gray-100 text-sm rounded-full hover:bg-gray-200",
708
+ children: folder.title
709
+ },
710
+ folder.slug
711
+ )) })
712
+ ] });
713
+ };
714
+
715
+ // src/components/author-page/index.tsx
716
+ var import_image3 = __toESM(require("next/image"), 1);
717
+ var import_jsx_runtime10 = require("react/jsx-runtime");
718
+ var AuthorPage = ({
719
+ author
720
+ }) => {
721
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "max-w-5xl mx-auto px-4 py-10", children: [
722
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
723
+ BreadCrumbs,
724
+ {
725
+ breadcrumbs: [
726
+ {
727
+ name: "Authors",
728
+ url: `${opinlyConfig.blogPrefix}/authors`
729
+ }
730
+ ]
731
+ }
732
+ ),
733
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-6 mb-10", children: [
734
+ author.image.fileKey && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
735
+ import_image3.default,
736
+ {
737
+ src: `${opinlyConfig.imagesPrefix}/${replaceImageNameSpace(author.image.fileKey)}`,
738
+ alt: author.image.alt || author.name,
739
+ width: 150,
740
+ height: 150,
741
+ className: "rounded-full border"
742
+ }
743
+ ),
744
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
745
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { className: "text-3xl font-bold mb-1", children: author.name }),
746
+ author.bio && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-muted-foreground text-base max-w-2xl", children: author.bio })
747
+ ] })
748
+ ] }),
749
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("h2", { className: "text-xl font-semibold mb-6", children: [
750
+ "Posts by ",
751
+ author.name
752
+ ] }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8", children: author.posts && author.posts.length > 0 ? author.posts.map(
754
+ (post) => post && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PostPreview, { post }, post.slug)
755
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "text-muted-foreground col-span-full", children: "No posts yet." }) })
756
+ ] });
757
+ };
758
+
759
+ // src/components/not-found.tsx
760
+ var import_link8 = __toESM(require("next/link"), 1);
761
+ var import_jsx_runtime11 = require("react/jsx-runtime");
762
+ var OpinlyNotFound = () => {
763
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
764
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "bg-orange-600 py-10", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "max-w-7xl mx-auto px-4", children: [
765
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h1", { className: "text-4xl font-bold mb-2 leading-tight text-white", children: "Post Not Found" }),
766
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-sm mb-4 text-orange-100", children: "The post you're looking for doesn't exist or has been removed." })
767
+ ] }) }),
768
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex max-w-7xl mx-auto mt-10 gap-12", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("article", { className: "w-2/3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "prose max-w-none", children: [
769
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-gray-600", children: "You can try:" }),
770
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("ul", { className: "list-disc pl-6 my-4", children: [
771
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { children: "Checking the URL for typos" }),
772
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("li", { children: [
773
+ "Returning to the",
774
+ " ",
775
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_link8.default, { href: "/blog", className: "text-blue-600 underline", children: "blog homepage" })
776
+ ] }),
777
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("li", { children: "Using the search function to find what you're looking for" })
778
+ ] })
779
+ ] }) }) })
780
+ ] });
781
+ };
782
+
783
+ // src/components/authors-page/index.tsx
784
+ var import_image4 = __toESM(require("next/image"), 1);
785
+ var import_link9 = __toESM(require("next/link"), 1);
786
+ var import_jsx_runtime12 = require("react/jsx-runtime");
787
+ var AuthorsPage = ({
788
+ authors
789
+ }) => {
790
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "max-w-5xl mx-auto px-4 py-10", children: [
791
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
792
+ BreadCrumbs,
793
+ {
794
+ breadcrumbs: [
795
+ {
796
+ name: "Authors"
797
+ }
798
+ ]
799
+ }
800
+ ),
801
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h1", { className: "text-3xl font-bold mb-10", children: "Authors" }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "space-y-16", children: authors.map((author, idx) => {
803
+ const authorSlug = `${opinlyConfig.blogPrefix}/authors/${author.slug}`;
804
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
805
+ "div",
806
+ {
807
+ className: "rounded-lg p-6 flex flex-col gap-6",
808
+ children: [
809
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-6 mb-4", children: [
810
+ author.image?.fileKey && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_link9.default, { href: authorSlug, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
811
+ import_image4.default,
812
+ {
813
+ src: `${opinlyConfig.imagesPrefix}/${replaceImageNameSpace(author.image.fileKey)}`,
814
+ alt: author.image.alt || author.name,
815
+ width: 100,
816
+ height: 100,
817
+ className: "rounded-full border"
818
+ }
819
+ ) }),
820
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_link9.default, { href: authorSlug, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "text-2xl font-semibold mb-1", children: author.name }) }),
822
+ author.bio && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-muted-foreground text-base max-w-2xl", children: author.bio })
823
+ ] })
824
+ ] }),
825
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
826
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mb-4", children: author.posts && author.posts.length > 0 ? author.posts.map(
827
+ (post) => post && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PostPreview, { post }, post.slug)
828
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "text-muted-foreground col-span-full", children: "No posts yet." }) }),
829
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
830
+ import_link9.default,
831
+ {
832
+ href: `${opinlyConfig.blogPrefix}/authors/${author.slug}`,
833
+ className: "text-bold",
834
+ children: [
835
+ "View more posts from ",
836
+ author.name
837
+ ]
838
+ }
839
+ ) })
840
+ ] })
841
+ ]
842
+ },
843
+ author.name + idx
844
+ );
845
+ }) })
846
+ ] });
847
+ };
848
+
849
+ // src/components/opinly-blog.tsx
850
+ var import_jsx_runtime13 = require("react/jsx-runtime");
851
+ var OpinlyBlog = ({
852
+ blog,
853
+ page
854
+ }) => {
855
+ if (blog.type === "not-found") {
856
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(OpinlyNotFound, {});
857
+ }
858
+ if (blog.type === "home") {
859
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HomePage, { homePage: blog.data, page });
860
+ }
861
+ if (blog.type === "post") {
862
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(BlogPost, { post: blog.data });
863
+ }
864
+ if (blog.type === "author") {
865
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AuthorPage, { author: blog.data });
866
+ }
867
+ if (blog.type === "authors") {
868
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(AuthorsPage, { authors: blog.data });
869
+ }
870
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FolderPage, { folder: blog.data });
871
+ };
872
+ // Annotate the CommonJS export names for ESM import in node:
873
+ 0 && (module.exports = {
874
+ HomePage,
875
+ OpinlyBlog
876
+ });