@uptrademedia/site-kit 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.
Files changed (120) hide show
  1. package/README.md +305 -0
  2. package/dist/analytics/index.js +88 -0
  3. package/dist/analytics/index.js.map +1 -0
  4. package/dist/analytics/index.mjs +70 -0
  5. package/dist/analytics/index.mjs.map +1 -0
  6. package/dist/api-N35S3EES.js +57 -0
  7. package/dist/api-N35S3EES.js.map +1 -0
  8. package/dist/api-SYBTK7Z7.mjs +4 -0
  9. package/dist/api-SYBTK7Z7.mjs.map +1 -0
  10. package/dist/blog/index.js +200 -0
  11. package/dist/blog/index.js.map +1 -0
  12. package/dist/blog/index.mjs +194 -0
  13. package/dist/blog/index.mjs.map +1 -0
  14. package/dist/chunk-3MUOUXHV.js +3721 -0
  15. package/dist/chunk-3MUOUXHV.js.map +1 -0
  16. package/dist/chunk-4HVYXYQL 2.mjs +255 -0
  17. package/dist/chunk-4HVYXYQL.mjs +255 -0
  18. package/dist/chunk-4HVYXYQL.mjs.map +1 -0
  19. package/dist/chunk-7H6I3ECV.mjs +120 -0
  20. package/dist/chunk-7H6I3ECV.mjs.map +1 -0
  21. package/dist/chunk-COI6GOX2.mjs +3679 -0
  22. package/dist/chunk-COI6GOX2.mjs.map +1 -0
  23. package/dist/chunk-EQCVQC35.js +35 -0
  24. package/dist/chunk-EQCVQC35.js 2.map +1 -0
  25. package/dist/chunk-EQCVQC35.js.map +1 -0
  26. package/dist/chunk-FEBYQGY4 2.mjs +251 -0
  27. package/dist/chunk-FEBYQGY4.mjs +251 -0
  28. package/dist/chunk-FEBYQGY4.mjs.map +1 -0
  29. package/dist/chunk-FKVJOT2F.mjs +796 -0
  30. package/dist/chunk-FKVJOT2F.mjs.map +1 -0
  31. package/dist/chunk-GQ6ZOU2N.mjs +134 -0
  32. package/dist/chunk-GQ6ZOU2N.mjs.map +1 -0
  33. package/dist/chunk-HCFPU7TU.js +137 -0
  34. package/dist/chunk-HCFPU7TU.js.map +1 -0
  35. package/dist/chunk-NYKRE2FL 2.mjs +31 -0
  36. package/dist/chunk-NYKRE2FL.mjs +31 -0
  37. package/dist/chunk-NYKRE2FL.mjs 2.map +1 -0
  38. package/dist/chunk-NYKRE2FL.mjs.map +1 -0
  39. package/dist/chunk-QP5NCO2E.js +133 -0
  40. package/dist/chunk-QP5NCO2E.js.map +1 -0
  41. package/dist/chunk-RV7H3I6J.js +255 -0
  42. package/dist/chunk-RV7H3I6J.js 2.map +1 -0
  43. package/dist/chunk-RV7H3I6J.js.map +1 -0
  44. package/dist/chunk-SBVEYCSV.js +140 -0
  45. package/dist/chunk-SBVEYCSV.js.map +1 -0
  46. package/dist/chunk-TUKGA3UK.js +257 -0
  47. package/dist/chunk-TUKGA3UK.js 2.map +1 -0
  48. package/dist/chunk-TUKGA3UK.js.map +1 -0
  49. package/dist/chunk-V3F5J6CV.js +801 -0
  50. package/dist/chunk-V3F5J6CV.js.map +1 -0
  51. package/dist/chunk-WPSRS352.mjs +135 -0
  52. package/dist/chunk-WPSRS352.mjs.map +1 -0
  53. package/dist/commerce/index.js +157 -0
  54. package/dist/commerce/index.js.map +1 -0
  55. package/dist/commerce/index.mjs +4 -0
  56. package/dist/commerce/index.mjs.map +1 -0
  57. package/dist/commerce/server.js +186 -0
  58. package/dist/commerce/server.js.map +1 -0
  59. package/dist/commerce/server.mjs +176 -0
  60. package/dist/commerce/server.mjs.map +1 -0
  61. package/dist/engage/index.js +50 -0
  62. package/dist/engage/index.js.map +1 -0
  63. package/dist/engage/index.mjs +44 -0
  64. package/dist/engage/index.mjs.map +1 -0
  65. package/dist/forms/index.js +1053 -0
  66. package/dist/forms/index.js.map +1 -0
  67. package/dist/forms/index.mjs +1035 -0
  68. package/dist/forms/index.mjs.map +1 -0
  69. package/dist/generators-7Y5ABRYV 2.mjs +161 -0
  70. package/dist/generators-7Y5ABRYV.mjs +161 -0
  71. package/dist/generators-7Y5ABRYV.mjs 2.map +1 -0
  72. package/dist/generators-7Y5ABRYV.mjs.map +1 -0
  73. package/dist/generators-GWIYCA5M.js +171 -0
  74. package/dist/generators-GWIYCA5M.js 2.map +1 -0
  75. package/dist/generators-GWIYCA5M.js.map +1 -0
  76. package/dist/index 2.mjs +74 -0
  77. package/dist/index.js +326 -0
  78. package/dist/index.js 2.map +1 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/index.mjs +222 -0
  81. package/dist/index.mjs.map +1 -0
  82. package/dist/migrator-V6KS75EA 2.mjs +265 -0
  83. package/dist/migrator-V6KS75EA.mjs +265 -0
  84. package/dist/migrator-V6KS75EA.mjs 2.map +1 -0
  85. package/dist/migrator-V6KS75EA.mjs.map +1 -0
  86. package/dist/migrator-XKM7YQCY.js +272 -0
  87. package/dist/migrator-XKM7YQCY.js 2.map +1 -0
  88. package/dist/migrator-XKM7YQCY.js.map +1 -0
  89. package/dist/scanner-MF7P3CDE 2.mjs +14386 -0
  90. package/dist/scanner-MF7P3CDE.mjs +14386 -0
  91. package/dist/scanner-MF7P3CDE.mjs 2.map +1 -0
  92. package/dist/scanner-MF7P3CDE.mjs.map +1 -0
  93. package/dist/scanner-NT6YG4TD 2.js +14397 -0
  94. package/dist/scanner-NT6YG4TD.js +14397 -0
  95. package/dist/scanner-NT6YG4TD.js 2.map +1 -0
  96. package/dist/scanner-NT6YG4TD.js.map +1 -0
  97. package/dist/seo/index.js +447 -0
  98. package/dist/seo/index.js.map +1 -0
  99. package/dist/seo/index.mjs +411 -0
  100. package/dist/seo/index.mjs.map +1 -0
  101. package/dist/seo/server.js +66 -0
  102. package/dist/seo/server.js.map +1 -0
  103. package/dist/seo/server.mjs +5 -0
  104. package/dist/seo/server.mjs.map +1 -0
  105. package/dist/setup/index.js +1050 -0
  106. package/dist/setup/index.js.map +1 -0
  107. package/dist/setup/index.mjs +1046 -0
  108. package/dist/setup/index.mjs.map +1 -0
  109. package/dist/sitemap/index.js +212 -0
  110. package/dist/sitemap/index.js.map +1 -0
  111. package/dist/sitemap/index.mjs +206 -0
  112. package/dist/sitemap/index.mjs.map +1 -0
  113. package/dist/web-vitals-BH55V7EJ.js +252 -0
  114. package/dist/web-vitals-BH55V7EJ.js 2.map +1 -0
  115. package/dist/web-vitals-BH55V7EJ.js.map +1 -0
  116. package/dist/web-vitals-RJYPWAR3 2.mjs +241 -0
  117. package/dist/web-vitals-RJYPWAR3.mjs +241 -0
  118. package/dist/web-vitals-RJYPWAR3.mjs 2.map +1 -0
  119. package/dist/web-vitals-RJYPWAR3.mjs.map +1 -0
  120. package/package.json +118 -0
@@ -0,0 +1,200 @@
1
+ 'use strict';
2
+
3
+ require('../chunk-EQCVQC35.js');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var react = require('react');
6
+
7
+ async function BlogPost({ projectId, slug, children }) {
8
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
9
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
10
+ "Blog post: ",
11
+ slug
12
+ ] }),
13
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Coming soon..." })
14
+ ] });
15
+ }
16
+ async function BlogList({ projectId, options, children }) {
17
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
18
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
19
+ "Blog list for project: ",
20
+ projectId
21
+ ] }),
22
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Coming soon..." })
23
+ ] });
24
+ }
25
+ function AuthorCard({ author, showBio = true, showSocial = true, className }) {
26
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { display: "flex", gap: 16, alignItems: "flex-start" }, children: [
27
+ author.avatar_url && /* @__PURE__ */ jsxRuntime.jsx(
28
+ "img",
29
+ {
30
+ src: author.avatar_url,
31
+ alt: author.name,
32
+ style: {
33
+ width: 64,
34
+ height: 64,
35
+ borderRadius: "50%",
36
+ objectFit: "cover"
37
+ }
38
+ }
39
+ ),
40
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
41
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { style: { margin: 0 }, children: author.name }),
42
+ showBio && author.bio && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "4px 0", color: "#6b7280", fontSize: 14 }, children: author.bio }),
43
+ showSocial && author.social_links && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 12, marginTop: 8 }, children: [
44
+ author.social_links.twitter && /* @__PURE__ */ jsxRuntime.jsx("a", { href: `https://twitter.com/${author.social_links.twitter}`, target: "_blank", rel: "noopener noreferrer", children: "Twitter" }),
45
+ author.social_links.linkedin && /* @__PURE__ */ jsxRuntime.jsx("a", { href: author.social_links.linkedin, target: "_blank", rel: "noopener noreferrer", children: "LinkedIn" }),
46
+ author.social_links.github && /* @__PURE__ */ jsxRuntime.jsx("a", { href: `https://github.com/${author.social_links.github}`, target: "_blank", rel: "noopener noreferrer", children: "GitHub" })
47
+ ] })
48
+ ] })
49
+ ] });
50
+ }
51
+ async function fetchRelatedPosts(apiUrl, apiKey, currentPostId, limit) {
52
+ try {
53
+ const response = await fetch(`${apiUrl}/api/public/blog/related`, {
54
+ method: "POST",
55
+ headers: {
56
+ "Content-Type": "application/json",
57
+ "x-api-key": apiKey
58
+ },
59
+ body: JSON.stringify({
60
+ currentPostId,
61
+ limit
62
+ })
63
+ });
64
+ if (!response.ok) {
65
+ console.error("Failed to fetch related posts:", response.statusText);
66
+ return [];
67
+ }
68
+ const data = await response.json();
69
+ return data.posts || [];
70
+ } catch (error) {
71
+ console.error("Failed to fetch related posts:", error);
72
+ return [];
73
+ }
74
+ }
75
+ async function RelatedPosts({
76
+ apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com",
77
+ apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY || "",
78
+ currentPostId,
79
+ limit = 3,
80
+ className,
81
+ renderItem
82
+ }) {
83
+ if (!apiKey) {
84
+ console.warn("[Blog] No API key configured for RelatedPosts");
85
+ return null;
86
+ }
87
+ const posts = await fetchRelatedPosts(apiUrl, apiKey, currentPostId, limit);
88
+ if (posts.length === 0) return null;
89
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className, children: [
90
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { style: { marginBottom: 16 }, children: "Related Posts" }),
91
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gap: 16 }, children: posts.map(
92
+ (post) => renderItem ? renderItem(post) : /* @__PURE__ */ jsxRuntime.jsxs("article", { style: { borderBottom: "1px solid #e5e7eb", paddingBottom: 16 }, children: [
93
+ post.featured_image && /* @__PURE__ */ jsxRuntime.jsx(
94
+ "img",
95
+ {
96
+ src: post.featured_image,
97
+ alt: post.title,
98
+ style: {
99
+ width: "100%",
100
+ height: 120,
101
+ objectFit: "cover",
102
+ borderRadius: 8,
103
+ marginBottom: 8
104
+ }
105
+ }
106
+ ),
107
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: `/blog/${post.slug}`, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx("h4", { style: { margin: 0, color: "inherit" }, children: post.title }) }),
108
+ post.excerpt && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "4px 0 0", fontSize: 14, color: "#6b7280" }, children: post.excerpt })
109
+ ] }, post.id)
110
+ ) })
111
+ ] });
112
+ }
113
+ function parseHeadings(content) {
114
+ const headingRegex = /<h([2-4])[^>]*id="([^"]*)"[^>]*>(.*?)<\/h[2-4]>/gi;
115
+ const items = [];
116
+ let match;
117
+ while ((match = headingRegex.exec(content)) !== null) {
118
+ items.push({
119
+ level: parseInt(match[1]),
120
+ id: match[2],
121
+ text: match[3].replace(/<[^>]*>/g, "")
122
+ // Strip any nested HTML
123
+ });
124
+ }
125
+ return items;
126
+ }
127
+ function TableOfContents({
128
+ content,
129
+ className,
130
+ maxDepth = 3
131
+ }) {
132
+ const [activeId, setActiveId] = react.useState("");
133
+ const items = parseHeadings(content).filter((item) => item.level <= maxDepth);
134
+ react.useEffect(() => {
135
+ if (typeof window === "undefined") return;
136
+ const observer = new IntersectionObserver(
137
+ (entries) => {
138
+ entries.forEach((entry) => {
139
+ if (entry.isIntersecting) {
140
+ setActiveId(entry.target.id);
141
+ }
142
+ });
143
+ },
144
+ {
145
+ rootMargin: "-20% 0% -35% 0%",
146
+ threshold: 0
147
+ }
148
+ );
149
+ items.forEach((item) => {
150
+ const element = document.getElementById(item.id);
151
+ if (element) {
152
+ observer.observe(element);
153
+ }
154
+ });
155
+ return () => observer.disconnect();
156
+ }, [items]);
157
+ if (items.length === 0) return null;
158
+ const handleClick = (id) => {
159
+ const element = document.getElementById(id);
160
+ if (element) {
161
+ element.scrollIntoView({ behavior: "smooth" });
162
+ }
163
+ };
164
+ return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className, "aria-label": "Table of contents", children: [
165
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { style: { margin: "0 0 12px", fontSize: 14, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.05em" }, children: "On This Page" }),
166
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
167
+ "li",
168
+ {
169
+ style: {
170
+ paddingLeft: (item.level - 2) * 12,
171
+ marginBottom: 8
172
+ },
173
+ children: /* @__PURE__ */ jsxRuntime.jsx(
174
+ "button",
175
+ {
176
+ onClick: () => handleClick(item.id),
177
+ style: {
178
+ all: "unset",
179
+ cursor: "pointer",
180
+ fontSize: 14,
181
+ color: activeId === item.id ? "#2563eb" : "#6b7280",
182
+ fontWeight: activeId === item.id ? 500 : 400,
183
+ transition: "color 0.2s"
184
+ },
185
+ children: item.text
186
+ }
187
+ )
188
+ },
189
+ item.id
190
+ )) })
191
+ ] });
192
+ }
193
+
194
+ exports.AuthorCard = AuthorCard;
195
+ exports.BlogList = BlogList;
196
+ exports.BlogPost = BlogPost;
197
+ exports.RelatedPosts = RelatedPosts;
198
+ exports.TableOfContents = TableOfContents;
199
+ //# sourceMappingURL=index.js.map
200
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/blog/BlogPost.tsx","../../src/blog/BlogList.tsx","../../src/blog/AuthorCard.tsx","../../src/blog/RelatedPosts.tsx","../../src/blog/TableOfContents.tsx"],"names":["jsxs","jsx","useState","useEffect"],"mappings":";;;;;;AAOA,eAAsB,QAAA,CAAS,EAAE,SAAA,EAAW,IAAA,EAAM,UAAS,EAAkB;AAQ3E,EAAA,uCACG,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAAA,eAAA,CAAC,GAAA,EAAA,EAAE,QAAA,EAAA;AAAA,MAAA,aAAA;AAAA,MAAY;AAAA,KAAA,EAAK,CAAA;AAAA,oBACpBC,cAAA,CAAC,OAAE,QAAA,EAAA,gBAAA,EAAc;AAAA,GAAA,EACnB,CAAA;AAEJ;ACdA,eAAsB,QAAA,CAAS,EAAE,SAAA,EAAW,OAAA,EAAS,UAAS,EAAkB;AAM9E,EAAA,uBACED,gBAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAAA,gBAAC,GAAA,EAAA,EAAE,QAAA,EAAA;AAAA,MAAA,yBAAA;AAAA,MAAwB;AAAA,KAAA,EAAU,CAAA;AAAA,oBACrCC,cAAAA,CAAC,GAAA,EAAA,EAAE,QAAA,EAAA,gBAAA,EAAc;AAAA,GAAA,EACnB,CAAA;AAEJ;ACZO,SAAS,UAAA,CAAW,EAAE,MAAA,EAAQ,OAAA,GAAU,MAAM,UAAA,GAAa,IAAA,EAAM,WAAU,EAAoB;AACpG,EAAA,uBACED,eAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAsB,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,GAAA,EAAK,EAAA,EAAI,UAAA,EAAY,YAAA,EAAa,EACpF,QAAA,EAAA;AAAA,IAAA,MAAA,CAAO,8BACNC,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,KAAK,MAAA,CAAO,UAAA;AAAA,QACZ,KAAK,MAAA,CAAO,IAAA;AAAA,QACZ,KAAA,EAAO;AAAA,UACL,KAAA,EAAO,EAAA;AAAA,UACP,MAAA,EAAQ,EAAA;AAAA,UACR,YAAA,EAAc,KAAA;AAAA,UACd,SAAA,EAAW;AAAA;AACb;AAAA,KACF;AAAA,oBAEFD,gBAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAAC,cAAAA,CAAC,QAAG,KAAA,EAAO,EAAE,QAAQ,CAAA,EAAE,EAAI,iBAAO,IAAA,EAAK,CAAA;AAAA,MACtC,WAAW,MAAA,CAAO,GAAA,oBACjBA,cAAAA,CAAC,OAAE,KAAA,EAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,OAAO,SAAA,EAAW,QAAA,EAAU,EAAA,EAAG,EAAI,iBAAO,GAAA,EAAI,CAAA;AAAA,MAE5E,UAAA,IAAc,MAAA,CAAO,YAAA,oBACpBD,gBAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,GAAA,EAAK,EAAA,EAAI,SAAA,EAAW,GAAE,EAClD,QAAA,EAAA;AAAA,QAAA,MAAA,CAAO,YAAA,CAAa,OAAA,oBACnBC,cAAAA,CAAC,OAAE,IAAA,EAAM,CAAA,oBAAA,EAAuB,MAAA,CAAO,YAAA,CAAa,OAAO,CAAA,CAAA,EAAI,MAAA,EAAO,QAAA,EAAS,GAAA,EAAI,uBAAsB,QAAA,EAAA,SAAA,EAEzG,CAAA;AAAA,QAED,MAAA,CAAO,YAAA,CAAa,QAAA,oBACnBA,eAAC,GAAA,EAAA,EAAE,IAAA,EAAM,MAAA,CAAO,YAAA,CAAa,QAAA,EAAU,MAAA,EAAO,QAAA,EAAS,GAAA,EAAI,uBAAsB,QAAA,EAAA,UAAA,EAEjF,CAAA;AAAA,QAED,OAAO,YAAA,CAAa,MAAA,oBACnBA,cAAAA,CAAC,OAAE,IAAA,EAAM,CAAA,mBAAA,EAAsB,MAAA,CAAO,YAAA,CAAa,MAAM,CAAA,CAAA,EAAI,MAAA,EAAO,QAAA,EAAS,GAAA,EAAI,uBAAsB,QAAA,EAAA,QAAA,EAEvG;AAAA,OAAA,EAEJ;AAAA,KAAA,EAEJ;AAAA,GAAA,EACF,CAAA;AAEJ;ACxCA,eAAe,iBAAA,CACb,MAAA,EACA,MAAA,EACA,aAAA,EACA,KAAA,EACqB;AACrB,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA,wBAAA,CAAA,EAA4B;AAAA,MAChE,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB,kBAAA;AAAA,QAChB,WAAA,EAAa;AAAA,OACf;AAAA,MACA,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,QACnB,aAAA;AAAA,QACA;AAAA,OACD;AAAA,KACF,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,OAAA,CAAQ,KAAA,CAAM,gCAAA,EAAkC,QAAA,CAAS,UAAU,CAAA;AACnE,MAAA,OAAO,EAAC;AAAA,IACV;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,OAAO,IAAA,CAAK,SAAS,EAAC;AAAA,EACxB,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,kCAAkC,KAAK,CAAA;AACrD,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAOA,eAAsB,YAAA,CAAa;AAAA,EACjC,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,2BAAA,IAA+B,8BAAA;AAAA,EACpD,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,2BAAA,IAA+B,EAAA;AAAA,EACpD,aAAA;AAAA,EACA,KAAA,GAAQ,CAAA;AAAA,EACR,SAAA;AAAA,EACA;AACF,CAAA,EAA4B;AAC1B,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAA,CAAQ,KAAK,+CAA+C,CAAA;AAC5D,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,QAAQ,MAAM,iBAAA,CAAkB,MAAA,EAAQ,MAAA,EAAQ,eAAe,KAAK,CAAA;AAE1E,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAE/B,EAAA,uBACED,eAAAA,CAAC,SAAA,EAAA,EAAQ,SAAA,EACP,QAAA,EAAA;AAAA,oBAAAC,eAAC,IAAA,EAAA,EAAG,KAAA,EAAO,EAAE,YAAA,EAAc,EAAA,IAAM,QAAA,EAAA,eAAA,EAAa,CAAA;AAAA,oBAC9CA,cAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,SAAS,MAAA,EAAQ,GAAA,EAAK,EAAA,EAAG,EACpC,QAAA,EAAA,KAAA,CAAM,GAAA;AAAA,MAAI,CAAC,IAAA,KACV,UAAA,GACE,UAAA,CAAW,IAAI,CAAA,mBAEfD,eAAAA,CAAC,SAAA,EAAA,EAAsB,OAAO,EAAE,YAAA,EAAc,mBAAA,EAAqB,aAAA,EAAe,IAAG,EAClF,QAAA,EAAA;AAAA,QAAA,IAAA,CAAK,kCACJC,cAAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,KAAK,IAAA,CAAK,cAAA;AAAA,YACV,KAAK,IAAA,CAAK,KAAA;AAAA,YACV,KAAA,EAAO;AAAA,cACL,KAAA,EAAO,MAAA;AAAA,cACP,MAAA,EAAQ,GAAA;AAAA,cACR,SAAA,EAAW,OAAA;AAAA,cACX,YAAA,EAAc,CAAA;AAAA,cACd,YAAA,EAAc;AAAA;AAChB;AAAA,SACF;AAAA,wBAEFA,cAAAA,CAAC,GAAA,EAAA,EAAE,IAAA,EAAM,CAAA,MAAA,EAAS,KAAK,IAAI,CAAA,CAAA,EAAI,KAAA,EAAO,EAAE,cAAA,EAAgB,MAAA,IACtD,QAAA,kBAAAA,cAAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAO,EAAE,MAAA,EAAQ,CAAA,EAAG,KAAA,EAAO,SAAA,EAAU,EAAI,QAAA,EAAA,IAAA,CAAK,KAAA,EAAM,CAAA,EAC1D,CAAA;AAAA,QACC,IAAA,CAAK,OAAA,oBACJA,cAAAA,CAAC,OAAE,KAAA,EAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,UAAU,EAAA,EAAI,KAAA,EAAO,SAAA,EAAU,EAC3D,eAAK,OAAA,EACR;AAAA,OAAA,EAAA,EApBU,KAAK,EAsBnB;AAAA,KAEJ,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;ACpFA,SAAS,cAAc,OAAA,EAA4B;AAEjD,EAAA,MAAM,YAAA,GAAe,mDAAA;AACrB,EAAA,MAAM,QAAmB,EAAC;AAC1B,EAAA,IAAI,KAAA;AAEJ,EAAA,OAAA,CAAQ,KAAA,GAAQ,YAAA,CAAa,IAAA,CAAK,OAAO,OAAO,IAAA,EAAM;AACpD,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,KAAA,EAAO,QAAA,CAAS,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,MACxB,EAAA,EAAI,MAAM,CAAC,CAAA;AAAA,MACX,MAAM,KAAA,CAAM,CAAC,CAAA,CAAE,OAAA,CAAQ,YAAY,EAAE;AAAA;AAAA,KACtC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,KAAA;AACT;AAEO,SAAS,eAAA,CAAgB;AAAA,EAC9B,OAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA,GAAW;AACb,CAAA,EAAyB;AACvB,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIC,eAAiB,EAAE,CAAA;AACnD,EAAA,MAAM,KAAA,GAAQ,cAAc,OAAO,CAAA,CAAE,OAAO,CAAC,IAAA,KAAS,IAAA,CAAK,KAAA,IAAS,QAAQ,CAAA;AAE5E,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAEnC,IAAA,MAAM,WAAW,IAAI,oBAAA;AAAA,MACnB,CAAC,OAAA,KAAY;AACX,QAAA,OAAA,CAAQ,OAAA,CAAQ,CAAC,KAAA,KAAU;AACzB,UAAA,IAAI,MAAM,cAAA,EAAgB;AACxB,YAAA,WAAA,CAAY,KAAA,CAAM,OAAO,EAAE,CAAA;AAAA,UAC7B;AAAA,QACF,CAAC,CAAA;AAAA,MACH,CAAA;AAAA,MACA;AAAA,QACE,UAAA,EAAY,iBAAA;AAAA,QACZ,SAAA,EAAW;AAAA;AACb,KACF;AAGA,IAAA,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,KAAS;AACtB,MAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,IAAA,CAAK,EAAE,CAAA;AAC/C,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,QAAA,CAAS,QAAQ,OAAO,CAAA;AAAA,MAC1B;AAAA,IACF,CAAC,CAAA;AAED,IAAA,OAAO,MAAM,SAAS,UAAA,EAAW;AAAA,EACnC,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAE/B,EAAA,MAAM,WAAA,GAAc,CAAC,EAAA,KAAe;AAClC,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,EAAE,CAAA;AAC1C,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAA,CAAQ,cAAA,CAAe,EAAE,QAAA,EAAU,QAAA,EAAU,CAAA;AAAA,IAC/C;AAAA,EACF,CAAA;AAEA,EAAA,uBACEH,eAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAsB,cAAW,mBAAA,EACpC,QAAA,EAAA;AAAA,oBAAAC,cAAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAO,EAAE,QAAQ,UAAA,EAAY,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,KAAK,aAAA,EAAe,WAAA,EAAa,aAAA,EAAe,QAAA,IAAY,QAAA,EAAA,cAAA,EAEvH,CAAA;AAAA,oBACAA,cAAAA,CAAC,IAAA,EAAA,EAAG,KAAA,EAAO,EAAE,WAAW,MAAA,EAAQ,MAAA,EAAQ,CAAA,EAAG,OAAA,EAAS,GAAE,EACnD,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,yBACVA,cAAAA;AAAA,MAAC,IAAA;AAAA,MAAA;AAAA,QAEC,KAAA,EAAO;AAAA,UACL,WAAA,EAAA,CAAc,IAAA,CAAK,KAAA,GAAQ,CAAA,IAAK,EAAA;AAAA,UAChC,YAAA,EAAc;AAAA,SAChB;AAAA,QAEA,QAAA,kBAAAA,cAAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAS,MAAM,WAAA,CAAY,IAAA,CAAK,EAAE,CAAA;AAAA,YAClC,KAAA,EAAO;AAAA,cACL,GAAA,EAAK,OAAA;AAAA,cACL,MAAA,EAAQ,SAAA;AAAA,cACR,QAAA,EAAU,EAAA;AAAA,cACV,KAAA,EAAO,QAAA,KAAa,IAAA,CAAK,EAAA,GAAK,SAAA,GAAY,SAAA;AAAA,cAC1C,UAAA,EAAY,QAAA,KAAa,IAAA,CAAK,EAAA,GAAK,GAAA,GAAM,GAAA;AAAA,cACzC,UAAA,EAAY;AAAA,aACd;AAAA,YAEC,QAAA,EAAA,IAAA,CAAK;AAAA;AAAA;AACR,OAAA;AAAA,MAlBK,IAAA,CAAK;AAAA,KAoBb,CAAA,EACH;AAAA,GAAA,EACF,CAAA;AAEJ","file":"index.js","sourcesContent":["/**\n * @uptrade/site-kit/blog - Blog Post Component (Placeholder)\n */\n\nimport React from 'react'\nimport type { BlogPostProps } from './types'\n\nexport async function BlogPost({ projectId, slug, children }: BlogPostProps) {\n // Placeholder - full implementation will:\n // 1. Fetch post from Supabase\n // 2. Parse content (markdown to HTML if needed)\n // 3. Generate table of contents\n // 4. Fetch related posts\n // 5. Track view analytics\n \n return (\n <div>\n <p>Blog post: {slug}</p>\n <p>Coming soon...</p>\n </div>\n )\n}\n","/**\n * @uptrade/site-kit/blog - Blog List Component (Placeholder)\n */\n\nimport React from 'react'\nimport type { BlogListProps } from './types'\n\nexport async function BlogList({ projectId, options, children }: BlogListProps) {\n // Placeholder - full implementation will:\n // 1. Fetch posts with pagination\n // 2. Apply filters (category, tag, search)\n // 3. Fetch categories and tags for filtering UI\n \n return (\n <div>\n <p>Blog list for project: {projectId}</p>\n <p>Coming soon...</p>\n </div>\n )\n}\n","/**\n * @uptrade/site-kit/blog - Author Card Component\n */\n\nimport React from 'react'\nimport type { AuthorCardProps } from './types'\n\nexport function AuthorCard({ author, showBio = true, showSocial = true, className }: AuthorCardProps) {\n return (\n <div className={className} style={{ display: 'flex', gap: 16, alignItems: 'flex-start' }}>\n {author.avatar_url && (\n <img\n src={author.avatar_url}\n alt={author.name}\n style={{\n width: 64,\n height: 64,\n borderRadius: '50%',\n objectFit: 'cover',\n }}\n />\n )}\n <div>\n <h4 style={{ margin: 0 }}>{author.name}</h4>\n {showBio && author.bio && (\n <p style={{ margin: '4px 0', color: '#6b7280', fontSize: 14 }}>{author.bio}</p>\n )}\n {showSocial && author.social_links && (\n <div style={{ display: 'flex', gap: 12, marginTop: 8 }}>\n {author.social_links.twitter && (\n <a href={`https://twitter.com/${author.social_links.twitter}`} target=\"_blank\" rel=\"noopener noreferrer\">\n Twitter\n </a>\n )}\n {author.social_links.linkedin && (\n <a href={author.social_links.linkedin} target=\"_blank\" rel=\"noopener noreferrer\">\n LinkedIn\n </a>\n )}\n {author.social_links.github && (\n <a href={`https://github.com/${author.social_links.github}`} target=\"_blank\" rel=\"noopener noreferrer\">\n GitHub\n </a>\n )}\n </div>\n )}\n </div>\n </div>\n )\n}\n","/**\n * @uptrade/site-kit/blog - Related Posts Component\n * \n * Fetches and displays related blog posts via Portal API\n */\n\nimport React from 'react'\nimport type { RelatedPostsProps, BlogPost } from './types'\n\nasync function fetchRelatedPosts(\n apiUrl: string,\n apiKey: string,\n currentPostId: string,\n limit: number\n): Promise<BlogPost[]> {\n try {\n const response = await fetch(`${apiUrl}/api/public/blog/related`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n currentPostId,\n limit,\n }),\n })\n \n if (!response.ok) {\n console.error('Failed to fetch related posts:', response.statusText)\n return []\n }\n \n const data = await response.json()\n return data.posts || []\n } catch (error) {\n console.error('Failed to fetch related posts:', error)\n return []\n }\n}\n\ninterface RelatedPostsServerProps extends Omit<RelatedPostsProps, 'projectId'> {\n apiUrl?: string\n apiKey?: string\n}\n\nexport async function RelatedPosts({\n apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com',\n apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY || '',\n currentPostId,\n limit = 3,\n className,\n renderItem,\n}: RelatedPostsServerProps) {\n if (!apiKey) {\n console.warn('[Blog] No API key configured for RelatedPosts')\n return null\n }\n \n const posts = await fetchRelatedPosts(apiUrl, apiKey, currentPostId, limit)\n\n if (posts.length === 0) return null\n\n return (\n <section className={className}>\n <h3 style={{ marginBottom: 16 }}>Related Posts</h3>\n <div style={{ display: 'grid', gap: 16 }}>\n {posts.map((post) =>\n renderItem ? (\n renderItem(post)\n ) : (\n <article key={post.id} style={{ borderBottom: '1px solid #e5e7eb', paddingBottom: 16 }}>\n {post.featured_image && (\n <img\n src={post.featured_image}\n alt={post.title}\n style={{\n width: '100%',\n height: 120,\n objectFit: 'cover',\n borderRadius: 8,\n marginBottom: 8,\n }}\n />\n )}\n <a href={`/blog/${post.slug}`} style={{ textDecoration: 'none' }}>\n <h4 style={{ margin: 0, color: 'inherit' }}>{post.title}</h4>\n </a>\n {post.excerpt && (\n <p style={{ margin: '4px 0 0', fontSize: 14, color: '#6b7280' }}>\n {post.excerpt}\n </p>\n )}\n </article>\n )\n )}\n </div>\n </section>\n )\n}\n","/**\n * @uptrade/site-kit/blog - Table of Contents Component\n */\n\n'use client'\n\nimport React, { useState, useEffect } from 'react'\nimport type { TableOfContentsProps } from './types'\n\ninterface TocItem {\n id: string\n text: string\n level: number\n}\n\nfunction parseHeadings(content: string): TocItem[] {\n // Extract headings from HTML content\n const headingRegex = /<h([2-4])[^>]*id=\"([^\"]*)\"[^>]*>(.*?)<\\/h[2-4]>/gi\n const items: TocItem[] = []\n let match\n\n while ((match = headingRegex.exec(content)) !== null) {\n items.push({\n level: parseInt(match[1]),\n id: match[2],\n text: match[3].replace(/<[^>]*>/g, ''), // Strip any nested HTML\n })\n }\n\n return items\n}\n\nexport function TableOfContents({\n content,\n className,\n maxDepth = 3,\n}: TableOfContentsProps) {\n const [activeId, setActiveId] = useState<string>('')\n const items = parseHeadings(content).filter((item) => item.level <= maxDepth)\n\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n setActiveId(entry.target.id)\n }\n })\n },\n {\n rootMargin: '-20% 0% -35% 0%',\n threshold: 0,\n }\n )\n\n // Observe all headings\n items.forEach((item) => {\n const element = document.getElementById(item.id)\n if (element) {\n observer.observe(element)\n }\n })\n\n return () => observer.disconnect()\n }, [items])\n\n if (items.length === 0) return null\n\n const handleClick = (id: string) => {\n const element = document.getElementById(id)\n if (element) {\n element.scrollIntoView({ behavior: 'smooth' })\n }\n }\n\n return (\n <nav className={className} aria-label=\"Table of contents\">\n <h4 style={{ margin: '0 0 12px', fontSize: 14, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n On This Page\n </h4>\n <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>\n {items.map((item) => (\n <li\n key={item.id}\n style={{\n paddingLeft: (item.level - 2) * 12,\n marginBottom: 8,\n }}\n >\n <button\n onClick={() => handleClick(item.id)}\n style={{\n all: 'unset',\n cursor: 'pointer',\n fontSize: 14,\n color: activeId === item.id ? '#2563eb' : '#6b7280',\n fontWeight: activeId === item.id ? 500 : 400,\n transition: 'color 0.2s',\n }}\n >\n {item.text}\n </button>\n </li>\n ))}\n </ul>\n </nav>\n )\n}\n"]}
@@ -0,0 +1,194 @@
1
+ import '../chunk-NYKRE2FL.mjs';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useState, useEffect } from 'react';
4
+
5
+ async function BlogPost({ projectId, slug, children }) {
6
+ return /* @__PURE__ */ jsxs("div", { children: [
7
+ /* @__PURE__ */ jsxs("p", { children: [
8
+ "Blog post: ",
9
+ slug
10
+ ] }),
11
+ /* @__PURE__ */ jsx("p", { children: "Coming soon..." })
12
+ ] });
13
+ }
14
+ async function BlogList({ projectId, options, children }) {
15
+ return /* @__PURE__ */ jsxs("div", { children: [
16
+ /* @__PURE__ */ jsxs("p", { children: [
17
+ "Blog list for project: ",
18
+ projectId
19
+ ] }),
20
+ /* @__PURE__ */ jsx("p", { children: "Coming soon..." })
21
+ ] });
22
+ }
23
+ function AuthorCard({ author, showBio = true, showSocial = true, className }) {
24
+ return /* @__PURE__ */ jsxs("div", { className, style: { display: "flex", gap: 16, alignItems: "flex-start" }, children: [
25
+ author.avatar_url && /* @__PURE__ */ jsx(
26
+ "img",
27
+ {
28
+ src: author.avatar_url,
29
+ alt: author.name,
30
+ style: {
31
+ width: 64,
32
+ height: 64,
33
+ borderRadius: "50%",
34
+ objectFit: "cover"
35
+ }
36
+ }
37
+ ),
38
+ /* @__PURE__ */ jsxs("div", { children: [
39
+ /* @__PURE__ */ jsx("h4", { style: { margin: 0 }, children: author.name }),
40
+ showBio && author.bio && /* @__PURE__ */ jsx("p", { style: { margin: "4px 0", color: "#6b7280", fontSize: 14 }, children: author.bio }),
41
+ showSocial && author.social_links && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 12, marginTop: 8 }, children: [
42
+ author.social_links.twitter && /* @__PURE__ */ jsx("a", { href: `https://twitter.com/${author.social_links.twitter}`, target: "_blank", rel: "noopener noreferrer", children: "Twitter" }),
43
+ author.social_links.linkedin && /* @__PURE__ */ jsx("a", { href: author.social_links.linkedin, target: "_blank", rel: "noopener noreferrer", children: "LinkedIn" }),
44
+ author.social_links.github && /* @__PURE__ */ jsx("a", { href: `https://github.com/${author.social_links.github}`, target: "_blank", rel: "noopener noreferrer", children: "GitHub" })
45
+ ] })
46
+ ] })
47
+ ] });
48
+ }
49
+ async function fetchRelatedPosts(apiUrl, apiKey, currentPostId, limit) {
50
+ try {
51
+ const response = await fetch(`${apiUrl}/api/public/blog/related`, {
52
+ method: "POST",
53
+ headers: {
54
+ "Content-Type": "application/json",
55
+ "x-api-key": apiKey
56
+ },
57
+ body: JSON.stringify({
58
+ currentPostId,
59
+ limit
60
+ })
61
+ });
62
+ if (!response.ok) {
63
+ console.error("Failed to fetch related posts:", response.statusText);
64
+ return [];
65
+ }
66
+ const data = await response.json();
67
+ return data.posts || [];
68
+ } catch (error) {
69
+ console.error("Failed to fetch related posts:", error);
70
+ return [];
71
+ }
72
+ }
73
+ async function RelatedPosts({
74
+ apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || "https://api.uptrademedia.com",
75
+ apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY || "",
76
+ currentPostId,
77
+ limit = 3,
78
+ className,
79
+ renderItem
80
+ }) {
81
+ if (!apiKey) {
82
+ console.warn("[Blog] No API key configured for RelatedPosts");
83
+ return null;
84
+ }
85
+ const posts = await fetchRelatedPosts(apiUrl, apiKey, currentPostId, limit);
86
+ if (posts.length === 0) return null;
87
+ return /* @__PURE__ */ jsxs("section", { className, children: [
88
+ /* @__PURE__ */ jsx("h3", { style: { marginBottom: 16 }, children: "Related Posts" }),
89
+ /* @__PURE__ */ jsx("div", { style: { display: "grid", gap: 16 }, children: posts.map(
90
+ (post) => renderItem ? renderItem(post) : /* @__PURE__ */ jsxs("article", { style: { borderBottom: "1px solid #e5e7eb", paddingBottom: 16 }, children: [
91
+ post.featured_image && /* @__PURE__ */ jsx(
92
+ "img",
93
+ {
94
+ src: post.featured_image,
95
+ alt: post.title,
96
+ style: {
97
+ width: "100%",
98
+ height: 120,
99
+ objectFit: "cover",
100
+ borderRadius: 8,
101
+ marginBottom: 8
102
+ }
103
+ }
104
+ ),
105
+ /* @__PURE__ */ jsx("a", { href: `/blog/${post.slug}`, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsx("h4", { style: { margin: 0, color: "inherit" }, children: post.title }) }),
106
+ post.excerpt && /* @__PURE__ */ jsx("p", { style: { margin: "4px 0 0", fontSize: 14, color: "#6b7280" }, children: post.excerpt })
107
+ ] }, post.id)
108
+ ) })
109
+ ] });
110
+ }
111
+ function parseHeadings(content) {
112
+ const headingRegex = /<h([2-4])[^>]*id="([^"]*)"[^>]*>(.*?)<\/h[2-4]>/gi;
113
+ const items = [];
114
+ let match;
115
+ while ((match = headingRegex.exec(content)) !== null) {
116
+ items.push({
117
+ level: parseInt(match[1]),
118
+ id: match[2],
119
+ text: match[3].replace(/<[^>]*>/g, "")
120
+ // Strip any nested HTML
121
+ });
122
+ }
123
+ return items;
124
+ }
125
+ function TableOfContents({
126
+ content,
127
+ className,
128
+ maxDepth = 3
129
+ }) {
130
+ const [activeId, setActiveId] = useState("");
131
+ const items = parseHeadings(content).filter((item) => item.level <= maxDepth);
132
+ useEffect(() => {
133
+ if (typeof window === "undefined") return;
134
+ const observer = new IntersectionObserver(
135
+ (entries) => {
136
+ entries.forEach((entry) => {
137
+ if (entry.isIntersecting) {
138
+ setActiveId(entry.target.id);
139
+ }
140
+ });
141
+ },
142
+ {
143
+ rootMargin: "-20% 0% -35% 0%",
144
+ threshold: 0
145
+ }
146
+ );
147
+ items.forEach((item) => {
148
+ const element = document.getElementById(item.id);
149
+ if (element) {
150
+ observer.observe(element);
151
+ }
152
+ });
153
+ return () => observer.disconnect();
154
+ }, [items]);
155
+ if (items.length === 0) return null;
156
+ const handleClick = (id) => {
157
+ const element = document.getElementById(id);
158
+ if (element) {
159
+ element.scrollIntoView({ behavior: "smooth" });
160
+ }
161
+ };
162
+ return /* @__PURE__ */ jsxs("nav", { className, "aria-label": "Table of contents", children: [
163
+ /* @__PURE__ */ jsx("h4", { style: { margin: "0 0 12px", fontSize: 14, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.05em" }, children: "On This Page" }),
164
+ /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: items.map((item) => /* @__PURE__ */ jsx(
165
+ "li",
166
+ {
167
+ style: {
168
+ paddingLeft: (item.level - 2) * 12,
169
+ marginBottom: 8
170
+ },
171
+ children: /* @__PURE__ */ jsx(
172
+ "button",
173
+ {
174
+ onClick: () => handleClick(item.id),
175
+ style: {
176
+ all: "unset",
177
+ cursor: "pointer",
178
+ fontSize: 14,
179
+ color: activeId === item.id ? "#2563eb" : "#6b7280",
180
+ fontWeight: activeId === item.id ? 500 : 400,
181
+ transition: "color 0.2s"
182
+ },
183
+ children: item.text
184
+ }
185
+ )
186
+ },
187
+ item.id
188
+ )) })
189
+ ] });
190
+ }
191
+
192
+ export { AuthorCard, BlogList, BlogPost, RelatedPosts, TableOfContents };
193
+ //# sourceMappingURL=index.mjs.map
194
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/blog/BlogPost.tsx","../../src/blog/BlogList.tsx","../../src/blog/AuthorCard.tsx","../../src/blog/RelatedPosts.tsx","../../src/blog/TableOfContents.tsx"],"names":["jsxs","jsx"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @uptrade/site-kit/blog - Blog Post Component (Placeholder)\n */\n\nimport React from 'react'\nimport type { BlogPostProps } from './types'\n\nexport async function BlogPost({ projectId, slug, children }: BlogPostProps) {\n // Placeholder - full implementation will:\n // 1. Fetch post from Supabase\n // 2. Parse content (markdown to HTML if needed)\n // 3. Generate table of contents\n // 4. Fetch related posts\n // 5. Track view analytics\n \n return (\n <div>\n <p>Blog post: {slug}</p>\n <p>Coming soon...</p>\n </div>\n )\n}\n","/**\n * @uptrade/site-kit/blog - Blog List Component (Placeholder)\n */\n\nimport React from 'react'\nimport type { BlogListProps } from './types'\n\nexport async function BlogList({ projectId, options, children }: BlogListProps) {\n // Placeholder - full implementation will:\n // 1. Fetch posts with pagination\n // 2. Apply filters (category, tag, search)\n // 3. Fetch categories and tags for filtering UI\n \n return (\n <div>\n <p>Blog list for project: {projectId}</p>\n <p>Coming soon...</p>\n </div>\n )\n}\n","/**\n * @uptrade/site-kit/blog - Author Card Component\n */\n\nimport React from 'react'\nimport type { AuthorCardProps } from './types'\n\nexport function AuthorCard({ author, showBio = true, showSocial = true, className }: AuthorCardProps) {\n return (\n <div className={className} style={{ display: 'flex', gap: 16, alignItems: 'flex-start' }}>\n {author.avatar_url && (\n <img\n src={author.avatar_url}\n alt={author.name}\n style={{\n width: 64,\n height: 64,\n borderRadius: '50%',\n objectFit: 'cover',\n }}\n />\n )}\n <div>\n <h4 style={{ margin: 0 }}>{author.name}</h4>\n {showBio && author.bio && (\n <p style={{ margin: '4px 0', color: '#6b7280', fontSize: 14 }}>{author.bio}</p>\n )}\n {showSocial && author.social_links && (\n <div style={{ display: 'flex', gap: 12, marginTop: 8 }}>\n {author.social_links.twitter && (\n <a href={`https://twitter.com/${author.social_links.twitter}`} target=\"_blank\" rel=\"noopener noreferrer\">\n Twitter\n </a>\n )}\n {author.social_links.linkedin && (\n <a href={author.social_links.linkedin} target=\"_blank\" rel=\"noopener noreferrer\">\n LinkedIn\n </a>\n )}\n {author.social_links.github && (\n <a href={`https://github.com/${author.social_links.github}`} target=\"_blank\" rel=\"noopener noreferrer\">\n GitHub\n </a>\n )}\n </div>\n )}\n </div>\n </div>\n )\n}\n","/**\n * @uptrade/site-kit/blog - Related Posts Component\n * \n * Fetches and displays related blog posts via Portal API\n */\n\nimport React from 'react'\nimport type { RelatedPostsProps, BlogPost } from './types'\n\nasync function fetchRelatedPosts(\n apiUrl: string,\n apiKey: string,\n currentPostId: string,\n limit: number\n): Promise<BlogPost[]> {\n try {\n const response = await fetch(`${apiUrl}/api/public/blog/related`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({\n currentPostId,\n limit,\n }),\n })\n \n if (!response.ok) {\n console.error('Failed to fetch related posts:', response.statusText)\n return []\n }\n \n const data = await response.json()\n return data.posts || []\n } catch (error) {\n console.error('Failed to fetch related posts:', error)\n return []\n }\n}\n\ninterface RelatedPostsServerProps extends Omit<RelatedPostsProps, 'projectId'> {\n apiUrl?: string\n apiKey?: string\n}\n\nexport async function RelatedPosts({\n apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com',\n apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY || '',\n currentPostId,\n limit = 3,\n className,\n renderItem,\n}: RelatedPostsServerProps) {\n if (!apiKey) {\n console.warn('[Blog] No API key configured for RelatedPosts')\n return null\n }\n \n const posts = await fetchRelatedPosts(apiUrl, apiKey, currentPostId, limit)\n\n if (posts.length === 0) return null\n\n return (\n <section className={className}>\n <h3 style={{ marginBottom: 16 }}>Related Posts</h3>\n <div style={{ display: 'grid', gap: 16 }}>\n {posts.map((post) =>\n renderItem ? (\n renderItem(post)\n ) : (\n <article key={post.id} style={{ borderBottom: '1px solid #e5e7eb', paddingBottom: 16 }}>\n {post.featured_image && (\n <img\n src={post.featured_image}\n alt={post.title}\n style={{\n width: '100%',\n height: 120,\n objectFit: 'cover',\n borderRadius: 8,\n marginBottom: 8,\n }}\n />\n )}\n <a href={`/blog/${post.slug}`} style={{ textDecoration: 'none' }}>\n <h4 style={{ margin: 0, color: 'inherit' }}>{post.title}</h4>\n </a>\n {post.excerpt && (\n <p style={{ margin: '4px 0 0', fontSize: 14, color: '#6b7280' }}>\n {post.excerpt}\n </p>\n )}\n </article>\n )\n )}\n </div>\n </section>\n )\n}\n","/**\n * @uptrade/site-kit/blog - Table of Contents Component\n */\n\n'use client'\n\nimport React, { useState, useEffect } from 'react'\nimport type { TableOfContentsProps } from './types'\n\ninterface TocItem {\n id: string\n text: string\n level: number\n}\n\nfunction parseHeadings(content: string): TocItem[] {\n // Extract headings from HTML content\n const headingRegex = /<h([2-4])[^>]*id=\"([^\"]*)\"[^>]*>(.*?)<\\/h[2-4]>/gi\n const items: TocItem[] = []\n let match\n\n while ((match = headingRegex.exec(content)) !== null) {\n items.push({\n level: parseInt(match[1]),\n id: match[2],\n text: match[3].replace(/<[^>]*>/g, ''), // Strip any nested HTML\n })\n }\n\n return items\n}\n\nexport function TableOfContents({\n content,\n className,\n maxDepth = 3,\n}: TableOfContentsProps) {\n const [activeId, setActiveId] = useState<string>('')\n const items = parseHeadings(content).filter((item) => item.level <= maxDepth)\n\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n setActiveId(entry.target.id)\n }\n })\n },\n {\n rootMargin: '-20% 0% -35% 0%',\n threshold: 0,\n }\n )\n\n // Observe all headings\n items.forEach((item) => {\n const element = document.getElementById(item.id)\n if (element) {\n observer.observe(element)\n }\n })\n\n return () => observer.disconnect()\n }, [items])\n\n if (items.length === 0) return null\n\n const handleClick = (id: string) => {\n const element = document.getElementById(id)\n if (element) {\n element.scrollIntoView({ behavior: 'smooth' })\n }\n }\n\n return (\n <nav className={className} aria-label=\"Table of contents\">\n <h4 style={{ margin: '0 0 12px', fontSize: 14, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n On This Page\n </h4>\n <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>\n {items.map((item) => (\n <li\n key={item.id}\n style={{\n paddingLeft: (item.level - 2) * 12,\n marginBottom: 8,\n }}\n >\n <button\n onClick={() => handleClick(item.id)}\n style={{\n all: 'unset',\n cursor: 'pointer',\n fontSize: 14,\n color: activeId === item.id ? '#2563eb' : '#6b7280',\n fontWeight: activeId === item.id ? 500 : 400,\n transition: 'color 0.2s',\n }}\n >\n {item.text}\n </button>\n </li>\n ))}\n </ul>\n </nav>\n )\n}\n"]}