kofi-stack-template-generator 2.1.37 → 2.1.38

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 (323) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/dist/index.js +8057 -440
  3. package/package.json +1 -1
  4. package/src/templates.generated.ts +248 -94
  5. package/templates/integrations/posthog/src/components/providers/posthog-provider.tsx.hbs +4 -1
  6. package/templates/marketing/payload/package.json.hbs +41 -26
  7. package/templates/marketing/payload/src/Footer/Component.client.tsx +288 -0
  8. package/templates/marketing/payload/src/Footer/Component.tsx +11 -0
  9. package/templates/marketing/payload/src/Footer/RowLabel.tsx +15 -0
  10. package/templates/marketing/payload/src/Footer/config.ts +178 -0
  11. package/templates/marketing/payload/src/Footer/hooks/{revalidateFooter.ts.hbs → revalidateFooter.ts} +5 -5
  12. package/templates/marketing/payload/src/Header/Component.client.tsx +94 -0
  13. package/templates/marketing/payload/src/Header/Component.tsx +10 -0
  14. package/templates/marketing/payload/src/Header/MegaMenu/index.tsx +197 -0
  15. package/templates/marketing/payload/src/Header/MobileMenu/HamburgerIcon.tsx +48 -0
  16. package/templates/marketing/payload/src/Header/MobileMenu/index.tsx +299 -0
  17. package/templates/marketing/payload/src/Header/Nav/index.tsx +76 -0
  18. package/templates/marketing/payload/src/Header/RowLabel.tsx +21 -0
  19. package/templates/marketing/payload/src/Header/config.ts +208 -0
  20. package/templates/marketing/payload/src/Header/hooks/{revalidateHeader.ts.hbs → revalidateHeader.ts} +5 -5
  21. package/templates/marketing/payload/src/access/{authenticated.ts.hbs → authenticated.ts} +1 -1
  22. package/templates/marketing/payload/src/access/{authenticatedOrPublished.ts.hbs → authenticatedOrPublished.ts} +8 -8
  23. package/templates/marketing/payload/src/app/(docs)/docs/[[...slug]]/page.tsx +117 -0
  24. package/templates/marketing/payload/src/app/(docs)/docs/layout.tsx +39 -0
  25. package/templates/marketing/payload/src/app/(docs)/layout.tsx +44 -0
  26. package/templates/marketing/payload/src/app/(frontend)/(sitemaps)/pages-sitemap.xml/route.ts +68 -0
  27. package/templates/marketing/payload/src/app/(frontend)/(sitemaps)/posts-sitemap.xml/route.ts +55 -0
  28. package/templates/marketing/payload/src/app/(frontend)/[slug]/page.client.tsx +15 -0
  29. package/templates/marketing/payload/src/app/(frontend)/[slug]/page.tsx +114 -0
  30. package/templates/marketing/payload/src/app/(frontend)/api/docs-search/route.ts +67 -0
  31. package/templates/marketing/payload/src/app/(frontend)/api/newsletter/route.ts +260 -0
  32. package/templates/marketing/payload/src/app/(frontend)/api/pricing/route.ts +266 -0
  33. package/templates/marketing/payload/src/app/(frontend)/globals.css +1019 -0
  34. package/templates/marketing/payload/src/app/(frontend)/layout.tsx +114 -0
  35. package/templates/marketing/payload/src/app/(frontend)/next/exit-preview/route.ts +7 -0
  36. package/templates/marketing/payload/src/app/(frontend)/next/preview/route.ts +56 -0
  37. package/templates/marketing/payload/src/app/(frontend)/next/seed/route.ts +31 -0
  38. package/templates/marketing/payload/src/app/(frontend)/not-found.tsx +17 -0
  39. package/templates/marketing/payload/src/app/(frontend)/page.tsx +5 -0
  40. package/templates/marketing/payload/src/app/(frontend)/posts/BlogPageClient.tsx +190 -0
  41. package/templates/marketing/payload/src/app/(frontend)/posts/[slug]/BlogPostContent.tsx +67 -0
  42. package/templates/marketing/payload/src/app/(frontend)/posts/[slug]/page.client.tsx +15 -0
  43. package/templates/marketing/payload/src/app/(frontend)/posts/[slug]/page.tsx +118 -0
  44. package/templates/marketing/payload/src/app/(frontend)/posts/page/[pageNumber]/page.client.tsx +15 -0
  45. package/templates/marketing/payload/src/app/(frontend)/posts/page/[pageNumber]/page.tsx +87 -0
  46. package/templates/marketing/payload/src/app/(frontend)/posts/page.tsx +49 -0
  47. package/templates/marketing/payload/src/app/(frontend)/search/page.client.tsx +15 -0
  48. package/templates/marketing/payload/src/app/(frontend)/search/page.tsx +87 -0
  49. package/templates/marketing/payload/src/app/(payload)/admin/[[...segments]]/not-found.tsx +24 -0
  50. package/templates/marketing/payload/src/app/(payload)/admin/[[...segments]]/page.tsx +24 -0
  51. package/templates/marketing/payload/src/app/(payload)/admin/importMap.js +83 -0
  52. package/templates/marketing/payload/src/app/(payload)/api/[...slug]/{route.ts.hbs → route.ts} +13 -9
  53. package/templates/marketing/payload/src/app/(payload)/api/graphql/{route.ts.hbs → route.ts} +2 -2
  54. package/templates/marketing/payload/src/app/(payload)/api/graphql-playground/{route.ts.hbs → route.ts} +3 -3
  55. package/templates/marketing/payload/src/app/(payload)/custom.scss +0 -0
  56. package/templates/marketing/payload/src/app/(payload)/layout.tsx +31 -0
  57. package/templates/marketing/payload/src/blocks/ArchiveBlock/Component.tsx +65 -0
  58. package/templates/marketing/payload/src/blocks/ArchiveBlock/config.ts +120 -0
  59. package/templates/marketing/payload/src/blocks/Banner/Component.tsx +26 -0
  60. package/templates/marketing/payload/src/blocks/Banner/config.ts +67 -0
  61. package/templates/marketing/payload/src/blocks/BentoFeatures/Component.tsx +243 -0
  62. package/templates/marketing/payload/src/blocks/BentoFeatures/config.ts +147 -0
  63. package/templates/marketing/payload/src/blocks/CallToAction/Component.tsx +31 -0
  64. package/templates/marketing/payload/src/blocks/CallToAction/config.ts +68 -0
  65. package/templates/marketing/payload/src/blocks/Code/Component.client.tsx +33 -0
  66. package/templates/marketing/payload/src/blocks/Code/Component.tsx +21 -0
  67. package/templates/marketing/payload/src/blocks/Code/CopyButton.tsx +33 -0
  68. package/templates/marketing/payload/src/blocks/Code/config.ts +33 -0
  69. package/templates/marketing/payload/src/blocks/Content/Component.tsx +41 -0
  70. package/templates/marketing/payload/src/blocks/Content/config.ts +105 -0
  71. package/templates/marketing/payload/src/blocks/FAQAccordion/Component.tsx +90 -0
  72. package/templates/marketing/payload/src/blocks/FAQAccordion/config.ts +75 -0
  73. package/templates/marketing/payload/src/blocks/FeatureGrid/Component.tsx +108 -0
  74. package/templates/marketing/payload/src/blocks/FeatureGrid/config.ts +109 -0
  75. package/templates/marketing/payload/src/blocks/FeatureShowcase/Component.tsx +107 -0
  76. package/templates/marketing/payload/src/blocks/FeatureShowcase/config.ts +111 -0
  77. package/templates/marketing/payload/src/blocks/FinalCTA/Component.tsx +117 -0
  78. package/templates/marketing/payload/src/blocks/FinalCTA/config.ts +50 -0
  79. package/templates/marketing/payload/src/blocks/Form/Checkbox/index.tsx +45 -0
  80. package/templates/marketing/payload/src/blocks/Form/Component.tsx +170 -0
  81. package/templates/marketing/payload/src/blocks/Form/Country/index.tsx +65 -0
  82. package/templates/marketing/payload/src/blocks/Form/Country/options.ts +982 -0
  83. package/templates/marketing/payload/src/blocks/Form/Email/index.tsx +38 -0
  84. package/templates/marketing/payload/src/blocks/Form/Error/index.tsx +13 -0
  85. package/templates/marketing/payload/src/blocks/Form/Message/index.tsx +13 -0
  86. package/templates/marketing/payload/src/blocks/Form/Number/index.tsx +36 -0
  87. package/templates/marketing/payload/src/blocks/Form/Select/index.tsx +63 -0
  88. package/templates/marketing/payload/src/blocks/Form/State/index.tsx +64 -0
  89. package/templates/marketing/payload/src/blocks/Form/State/options.ts +52 -0
  90. package/templates/marketing/payload/src/blocks/Form/Text/index.tsx +32 -0
  91. package/templates/marketing/payload/src/blocks/Form/Textarea/index.tsx +40 -0
  92. package/templates/marketing/payload/src/blocks/Form/Width/index.tsx +13 -0
  93. package/templates/marketing/payload/src/blocks/Form/config.ts +77 -0
  94. package/templates/marketing/payload/src/blocks/Form/fields.tsx +21 -0
  95. package/templates/marketing/payload/src/blocks/HowItWorks/Component.tsx +59 -0
  96. package/templates/marketing/payload/src/blocks/HowItWorks/config.ts +88 -0
  97. package/templates/marketing/payload/src/blocks/IndustryTabs/Component.tsx +132 -0
  98. package/templates/marketing/payload/src/blocks/IndustryTabs/config.ts +77 -0
  99. package/templates/marketing/payload/src/blocks/LogoBanner/Component.tsx +95 -0
  100. package/templates/marketing/payload/src/blocks/LogoBanner/config.ts +48 -0
  101. package/templates/marketing/payload/src/blocks/MediaBlock/Component.tsx +67 -0
  102. package/templates/marketing/payload/src/blocks/MediaBlock/config.ts +14 -0
  103. package/templates/marketing/payload/src/blocks/Personas/Component.tsx +69 -0
  104. package/templates/marketing/payload/src/blocks/Personas/config.ts +96 -0
  105. package/templates/marketing/payload/src/blocks/PricingTable/ComparisonTable.tsx +250 -0
  106. package/templates/marketing/payload/src/blocks/PricingTable/Component.tsx +443 -0
  107. package/templates/marketing/payload/src/blocks/PricingTable/config.ts +142 -0
  108. package/templates/marketing/payload/src/blocks/ProofBanner/Component.tsx +65 -0
  109. package/templates/marketing/payload/src/blocks/ProofBanner/config.ts +42 -0
  110. package/templates/marketing/payload/src/blocks/RelatedPosts/Component.tsx +32 -0
  111. package/templates/marketing/payload/src/blocks/RenderBlocks.tsx +92 -0
  112. package/templates/marketing/payload/src/blocks/TestimonialsGrid/Component.tsx +107 -0
  113. package/templates/marketing/payload/src/blocks/TestimonialsGrid/config.ts +76 -0
  114. package/templates/marketing/payload/src/blocks/TrustColumns/Component.tsx +83 -0
  115. package/templates/marketing/payload/src/blocks/TrustColumns/config.ts +70 -0
  116. package/templates/marketing/payload/src/collections/Categories.ts +28 -0
  117. package/templates/marketing/payload/src/collections/FAQs/index.ts +100 -0
  118. package/templates/marketing/payload/src/collections/Media.ts +160 -0
  119. package/templates/marketing/payload/src/collections/Pages/hooks/revalidatePage.ts +43 -0
  120. package/templates/marketing/payload/src/collections/Pages/index.ts +168 -0
  121. package/templates/marketing/payload/src/collections/Posts/hooks/populateAuthors.ts +41 -0
  122. package/templates/marketing/payload/src/collections/Posts/hooks/revalidatePost.ts +44 -0
  123. package/templates/marketing/payload/src/collections/Posts/index.ts +259 -0
  124. package/templates/marketing/payload/src/collections/Users/index.ts +26 -0
  125. package/templates/marketing/payload/src/components/AdminBar/index.scss +7 -0
  126. package/templates/marketing/payload/src/components/AdminBar/index.tsx +89 -0
  127. package/templates/marketing/payload/src/components/Analytics/CTATracker.tsx +33 -0
  128. package/templates/marketing/payload/src/components/Analytics/FeatureSectionTracker.tsx +47 -0
  129. package/templates/marketing/payload/src/components/Analytics/PricingViewTracker.tsx +46 -0
  130. package/templates/marketing/payload/src/components/Analytics/index.tsx +3 -0
  131. package/templates/marketing/payload/src/components/BeforeDashboard/SeedButton/index.tsx +89 -0
  132. package/templates/marketing/payload/src/components/BeforeDashboard/index.tsx +69 -0
  133. package/templates/marketing/payload/src/components/BeforeLogin/index.tsx +14 -0
  134. package/templates/marketing/payload/src/components/BlogCTA/index.tsx +77 -0
  135. package/templates/marketing/payload/src/components/Card/index.tsx +85 -0
  136. package/templates/marketing/payload/src/components/CollectionArchive/index.tsx +32 -0
  137. package/templates/marketing/payload/src/components/JsonLd/index.tsx +138 -0
  138. package/templates/marketing/payload/src/components/Link/index.tsx +66 -0
  139. package/templates/marketing/payload/src/components/LivePreviewListener/index.tsx +10 -0
  140. package/templates/marketing/payload/src/components/Logo/Logo.tsx +46 -0
  141. package/templates/marketing/payload/src/components/Media/ImageMedia/index.tsx +80 -0
  142. package/templates/marketing/payload/src/components/Media/VideoMedia/index.tsx +47 -0
  143. package/templates/marketing/payload/src/components/Media/index.tsx +26 -0
  144. package/templates/marketing/payload/src/components/Media/types.ts +22 -0
  145. package/templates/marketing/payload/src/components/PageRange/index.tsx +57 -0
  146. package/templates/marketing/payload/src/components/Pagination/index.tsx +101 -0
  147. package/templates/marketing/payload/src/components/PayloadRedirects/index.tsx +48 -0
  148. package/templates/marketing/payload/src/components/RichText/index.tsx +152 -0
  149. package/templates/marketing/payload/src/components/TableOfContents/index.tsx +128 -0
  150. package/templates/marketing/payload/src/components/ui/accordion.tsx +64 -0
  151. package/templates/marketing/payload/src/components/ui/button.tsx +52 -0
  152. package/templates/marketing/payload/src/components/ui/card.tsx +48 -0
  153. package/templates/marketing/payload/src/components/ui/checkbox.tsx +27 -0
  154. package/templates/marketing/payload/src/components/ui/input.tsx +22 -0
  155. package/templates/marketing/payload/src/components/ui/label.tsx +19 -0
  156. package/templates/marketing/payload/src/components/ui/pagination.tsx +92 -0
  157. package/templates/marketing/payload/src/components/ui/select.tsx +144 -0
  158. package/templates/marketing/payload/src/components/ui/textarea.tsx +21 -0
  159. package/templates/marketing/payload/src/endpoints/seed/contact-form.ts +111 -0
  160. package/templates/marketing/payload/src/endpoints/seed/contact-page.ts +56 -0
  161. package/templates/marketing/payload/src/endpoints/seed/directoryhub/about.ts +281 -0
  162. package/templates/marketing/payload/src/endpoints/seed/directoryhub/faqs.ts +224 -0
  163. package/templates/marketing/payload/src/endpoints/seed/directoryhub/features/automation.ts +229 -0
  164. package/templates/marketing/payload/src/endpoints/seed/directoryhub/features/custom-fields.ts +229 -0
  165. package/templates/marketing/payload/src/endpoints/seed/directoryhub/features/dashboard.ts +228 -0
  166. package/templates/marketing/payload/src/endpoints/seed/directoryhub/features/index.ts +6 -0
  167. package/templates/marketing/payload/src/endpoints/seed/directoryhub/features/monetization.ts +230 -0
  168. package/templates/marketing/payload/src/endpoints/seed/directoryhub/features/seo.ts +229 -0
  169. package/templates/marketing/payload/src/endpoints/seed/directoryhub/features/templates.ts +218 -0
  170. package/templates/marketing/payload/src/endpoints/seed/directoryhub/home.ts +555 -0
  171. package/templates/marketing/payload/src/endpoints/seed/directoryhub/index.ts +767 -0
  172. package/templates/marketing/payload/src/endpoints/seed/directoryhub/posts.ts +623 -0
  173. package/templates/marketing/payload/src/endpoints/seed/directoryhub/pricing.ts +251 -0
  174. package/templates/marketing/payload/src/endpoints/seed/directoryhub/privacy.ts +457 -0
  175. package/templates/marketing/payload/src/endpoints/seed/directoryhub/richtext-helper.ts +88 -0
  176. package/templates/marketing/payload/src/endpoints/seed/directoryhub/terms.ts +478 -0
  177. package/templates/marketing/payload/src/endpoints/seed/directoryhub/use-cases/b2b-vendor-hubs.ts +229 -0
  178. package/templates/marketing/payload/src/endpoints/seed/directoryhub/use-cases/communities.ts +230 -0
  179. package/templates/marketing/payload/src/endpoints/seed/directoryhub/use-cases/index.ts +4 -0
  180. package/templates/marketing/payload/src/endpoints/seed/directoryhub/use-cases/local-services.ts +230 -0
  181. package/templates/marketing/payload/src/endpoints/seed/directoryhub/use-cases/marketplaces.ts +230 -0
  182. package/templates/marketing/payload/src/endpoints/seed/home-static.ts +691 -0
  183. package/templates/marketing/payload/src/endpoints/seed/home.ts +675 -0
  184. package/templates/marketing/payload/src/endpoints/seed/image-1.ts +67 -0
  185. package/templates/marketing/payload/src/endpoints/seed/image-2.ts +67 -0
  186. package/templates/marketing/payload/src/endpoints/seed/image-3.ts +67 -0
  187. package/templates/marketing/payload/src/endpoints/seed/image-hero-1.ts +5 -0
  188. package/templates/marketing/payload/src/endpoints/seed/image-hero1.webp +0 -0
  189. package/templates/marketing/payload/src/endpoints/seed/image-post1.webp +0 -0
  190. package/templates/marketing/payload/src/endpoints/seed/image-post2.webp +0 -0
  191. package/templates/marketing/payload/src/endpoints/seed/image-post3.webp +0 -0
  192. package/templates/marketing/payload/src/endpoints/seed/index.ts +335 -0
  193. package/templates/marketing/payload/src/endpoints/seed/post-1.ts +315 -0
  194. package/templates/marketing/payload/src/endpoints/seed/post-2.ts +232 -0
  195. package/templates/marketing/payload/src/endpoints/seed/post-3.ts +268 -0
  196. package/templates/marketing/payload/src/fields/defaultLexical.ts +73 -0
  197. package/templates/marketing/payload/src/fields/link.ts +139 -0
  198. package/templates/marketing/payload/src/fields/linkGroup.ts +28 -0
  199. package/templates/marketing/payload/src/heros/HighImpact/index.tsx +56 -0
  200. package/templates/marketing/payload/src/heros/LowImpact/index.tsx +48 -0
  201. package/templates/marketing/payload/src/heros/MediumImpact/index.tsx +50 -0
  202. package/templates/marketing/payload/src/heros/PostHero/index.tsx +73 -0
  203. package/templates/marketing/payload/src/heros/ProductShowcase/AnimatedMockup.tsx +241 -0
  204. package/templates/marketing/payload/src/heros/ProductShowcase/index.tsx +108 -0
  205. package/templates/marketing/payload/src/heros/{RenderHero.tsx.hbs → RenderHero.tsx} +9 -9
  206. package/templates/marketing/payload/src/heros/config.ts +121 -0
  207. package/templates/marketing/payload/src/hooks/populatePublishedAt.ts +15 -0
  208. package/templates/marketing/payload/src/hooks/{revalidateRedirects.ts.hbs → revalidateRedirects.ts} +3 -3
  209. package/templates/marketing/payload/src/lib/convex.ts +13 -0
  210. package/templates/marketing/payload/src/lib/docs-source.ts +138 -0
  211. package/templates/marketing/payload/src/lib/mdx.tsx +191 -0
  212. package/templates/marketing/payload/src/payload.config.ts.hbs +95 -145
  213. package/templates/marketing/payload/src/plugins/index.ts +107 -0
  214. package/templates/marketing/payload/src/providers/HeaderTheme/index.tsx +34 -0
  215. package/templates/marketing/payload/src/providers/PostHogProvider.tsx +33 -0
  216. package/templates/marketing/payload/src/providers/Theme/InitTheme/{index.tsx.hbs → index.tsx} +11 -10
  217. package/templates/marketing/payload/src/providers/Theme/ThemeSelector/index.tsx +133 -0
  218. package/templates/marketing/payload/src/providers/Theme/ThemeSelector/types.ts +7 -0
  219. package/templates/marketing/payload/src/providers/Theme/index.tsx +60 -0
  220. package/templates/marketing/payload/src/providers/Theme/shared.ts +17 -0
  221. package/templates/marketing/payload/src/providers/Theme/{types.ts.hbs → types.ts} +3 -3
  222. package/templates/marketing/payload/src/providers/index.tsx +17 -0
  223. package/templates/marketing/payload/src/search/Component.tsx +42 -0
  224. package/templates/marketing/payload/src/search/beforeSync.ts +56 -0
  225. package/templates/marketing/payload/src/search/fieldOverrides.ts +61 -0
  226. package/templates/marketing/payload/src/utilities/deepMerge.ts +35 -0
  227. package/templates/marketing/payload/src/utilities/extractHeadings.ts +78 -0
  228. package/templates/marketing/payload/src/utilities/formatAuthors.ts +24 -0
  229. package/templates/marketing/payload/src/utilities/formatDateTime.ts +20 -0
  230. package/templates/marketing/payload/src/utilities/generateMeta.ts +93 -0
  231. package/templates/marketing/payload/src/utilities/generatePreviewPath.ts +33 -0
  232. package/templates/marketing/payload/src/utilities/getDocument.ts +32 -0
  233. package/templates/marketing/payload/src/utilities/getGlobals.ts +26 -0
  234. package/templates/marketing/payload/src/utilities/getMeUser.ts +43 -0
  235. package/templates/marketing/payload/src/utilities/getMediaUrl.ts +24 -0
  236. package/templates/marketing/payload/src/utilities/getRedirects.ts +26 -0
  237. package/templates/marketing/payload/src/utilities/getURL.ts +26 -0
  238. package/templates/marketing/payload/src/utilities/mergeOpenGraph.ts +26 -0
  239. package/templates/marketing/payload/src/utilities/toKebabCase.ts +5 -0
  240. package/templates/marketing/payload/src/utilities/ui.ts +12 -0
  241. package/templates/marketing/payload/src/utilities/useClickableCard.ts +108 -0
  242. package/templates/marketing/payload/src/utilities/useDebounce.ts +17 -0
  243. package/templates/packages/ui/src/components/button.tsx.hbs +53 -0
  244. package/templates/packages/ui/src/components/card.tsx.hbs +76 -0
  245. package/templates/packages/ui/src/components/separator.tsx.hbs +26 -0
  246. package/templates/marketing/payload/src/Footer/config.ts.hbs +0 -178
  247. package/templates/marketing/payload/src/Footer/index.ts.hbs +0 -1
  248. package/templates/marketing/payload/src/Header/RowLabel.tsx.hbs +0 -21
  249. package/templates/marketing/payload/src/Header/config.ts.hbs +0 -208
  250. package/templates/marketing/payload/src/Header/index.ts.hbs +0 -1
  251. package/templates/marketing/payload/src/access/index.ts.hbs +0 -3
  252. package/templates/marketing/payload/src/app/(frontend)/layout.tsx.hbs +0 -19
  253. package/templates/marketing/payload/src/app/(frontend)/next/seed/route.ts.hbs +0 -31
  254. package/templates/marketing/payload/src/app/(frontend)/page.tsx.hbs +0 -83
  255. package/templates/marketing/payload/src/app/(payload)/admin/[[...segments]]/not-found.tsx.hbs +0 -24
  256. package/templates/marketing/payload/src/app/(payload)/admin/[[...segments]]/page.tsx.hbs +0 -24
  257. package/templates/marketing/payload/src/app/(payload)/admin/importMap.js.hbs +0 -1
  258. package/templates/marketing/payload/src/app/(payload)/custom.scss.hbs +0 -1
  259. package/templates/marketing/payload/src/app/(payload)/layout.tsx.hbs +0 -31
  260. package/templates/marketing/payload/src/app/globals.css.hbs +0 -83
  261. package/templates/marketing/payload/src/app/layout.tsx.hbs +0 -10
  262. package/templates/marketing/payload/src/blocks/Benefits.ts.hbs +0 -34
  263. package/templates/marketing/payload/src/blocks/CTA.ts.hbs +0 -39
  264. package/templates/marketing/payload/src/blocks/Content.ts.hbs +0 -9
  265. package/templates/marketing/payload/src/blocks/FAQ.ts.hbs +0 -18
  266. package/templates/marketing/payload/src/blocks/Features.ts.hbs +0 -34
  267. package/templates/marketing/payload/src/blocks/Hero.ts.hbs +0 -40
  268. package/templates/marketing/payload/src/blocks/LogoBanner.ts.hbs +0 -17
  269. package/templates/marketing/payload/src/blocks/Pricing.ts.hbs +0 -37
  270. package/templates/marketing/payload/src/blocks/Testimonials.ts.hbs +0 -21
  271. package/templates/marketing/payload/src/blocks/index.ts.hbs +0 -9
  272. package/templates/marketing/payload/src/collections/Categories/index.ts.hbs +0 -28
  273. package/templates/marketing/payload/src/collections/FAQs/index.ts.hbs +0 -100
  274. package/templates/marketing/payload/src/collections/Media.ts.hbs +0 -164
  275. package/templates/marketing/payload/src/collections/Pages/hooks/revalidatePage.ts.hbs +0 -43
  276. package/templates/marketing/payload/src/collections/Pages/index.ts.hbs +0 -142
  277. package/templates/marketing/payload/src/collections/Posts/hooks/populateAuthors.ts.hbs +0 -41
  278. package/templates/marketing/payload/src/collections/Posts/hooks/revalidatePost.ts.hbs +0 -44
  279. package/templates/marketing/payload/src/collections/Posts/index.ts.hbs +0 -244
  280. package/templates/marketing/payload/src/collections/Users/index.ts.hbs +0 -26
  281. package/templates/marketing/payload/src/collections/index.ts.hbs +0 -6
  282. package/templates/marketing/payload/src/components/BeforeDashboard/SeedButton/index.tsx.hbs +0 -89
  283. package/templates/marketing/payload/src/components/BeforeDashboard/index.tsx.hbs +0 -69
  284. package/templates/marketing/payload/src/components/BeforeLogin/index.tsx.hbs +0 -14
  285. package/templates/marketing/payload/src/components/Link/index.tsx.hbs +0 -79
  286. package/templates/marketing/payload/src/components/Media/index.tsx.hbs +0 -67
  287. package/templates/marketing/payload/src/components/RichText/index.tsx.hbs +0 -44
  288. package/templates/marketing/payload/src/endpoints/seed/home.ts.hbs +0 -76
  289. package/templates/marketing/payload/src/endpoints/seed/image-1.ts.hbs +0 -5
  290. package/templates/marketing/payload/src/endpoints/seed/image-2.ts.hbs +0 -5
  291. package/templates/marketing/payload/src/endpoints/seed/image-hero.ts.hbs +0 -5
  292. package/templates/marketing/payload/src/endpoints/seed/index.ts.hbs +0 -235
  293. package/templates/marketing/payload/src/endpoints/seed/post-1.ts.hbs +0 -252
  294. package/templates/marketing/payload/src/fields/defaultLexical.ts.hbs +0 -73
  295. package/templates/marketing/payload/src/fields/link.ts.hbs +0 -139
  296. package/templates/marketing/payload/src/fields/linkGroup.ts.hbs +0 -28
  297. package/templates/marketing/payload/src/globals/index.ts.hbs +0 -2
  298. package/templates/marketing/payload/src/heros/HighImpact/index.tsx.hbs +0 -53
  299. package/templates/marketing/payload/src/heros/LowImpact/index.tsx.hbs +0 -48
  300. package/templates/marketing/payload/src/heros/MediumImpact/index.tsx.hbs +0 -46
  301. package/templates/marketing/payload/src/heros/PostHero/index.tsx.hbs +0 -68
  302. package/templates/marketing/payload/src/heros/ProductShowcase/index.tsx.hbs +0 -88
  303. package/templates/marketing/payload/src/heros/config.ts.hbs +0 -112
  304. package/templates/marketing/payload/src/heros/index.ts.hbs +0 -7
  305. package/templates/marketing/payload/src/hooks/index.ts.hbs +0 -2
  306. package/templates/marketing/payload/src/hooks/populatePublishedAt.ts.hbs +0 -15
  307. package/templates/marketing/payload/src/providers/HeaderTheme/index.tsx.hbs +0 -34
  308. package/templates/marketing/payload/src/providers/Theme/index.tsx.hbs +0 -60
  309. package/templates/marketing/payload/src/providers/Theme/shared.ts.hbs +0 -17
  310. package/templates/marketing/payload/src/providers/index.tsx.hbs +0 -18
  311. package/templates/marketing/payload/src/utilities/deepMerge.ts.hbs +0 -35
  312. package/templates/marketing/payload/src/utilities/formatAuthors.ts.hbs +0 -24
  313. package/templates/marketing/payload/src/utilities/formatDateTime.ts.hbs +0 -13
  314. package/templates/marketing/payload/src/utilities/generateMeta.ts.hbs +0 -87
  315. package/templates/marketing/payload/src/utilities/generatePreviewPath.ts.hbs +0 -33
  316. package/templates/marketing/payload/src/utilities/getURL.ts.hbs +0 -26
  317. package/templates/marketing/payload/src/utilities/index.ts.hbs +0 -8
  318. package/templates/marketing/payload/src/utilities/mergeOpenGraph.ts.hbs +0 -26
  319. /package/templates/marketing/payload/src/access/{anyone.ts.hbs → anyone.ts} +0 -0
  320. /package/templates/marketing/payload/src/components/BeforeDashboard/SeedButton/{index.scss.hbs → index.scss} +0 -0
  321. /package/templates/marketing/payload/src/components/BeforeDashboard/{index.scss.hbs → index.scss} +0 -0
  322. /package/templates/marketing/payload/src/fields/{index.ts.hbs → index.ts} +0 -0
  323. /package/templates/marketing/payload/src/utilities/{canUseDOM.ts.hbs → canUseDOM.ts} +0 -0
@@ -0,0 +1,34 @@
1
+ "use client"
2
+
3
+ import type { Theme } from "@/providers/Theme/types"
4
+
5
+ import type React from "react"
6
+ import { createContext, use, useCallback, useState } from "react"
7
+
8
+ import canUseDOM from "@/utilities/canUseDOM"
9
+
10
+ export interface ContextType {
11
+ headerTheme?: Theme | null
12
+ setHeaderTheme: (theme: Theme | null) => void
13
+ }
14
+
15
+ const initialContext: ContextType = {
16
+ headerTheme: undefined,
17
+ setHeaderTheme: () => null,
18
+ }
19
+
20
+ const HeaderThemeContext = createContext(initialContext)
21
+
22
+ export const HeaderThemeProvider = ({ children }: { children: React.ReactNode }) => {
23
+ const [headerTheme, setThemeState] = useState<Theme | undefined | null>(
24
+ canUseDOM ? (document.documentElement.getAttribute("data-theme") as Theme) : undefined,
25
+ )
26
+
27
+ const setHeaderTheme = useCallback((themeToSet: Theme | null) => {
28
+ setThemeState(themeToSet)
29
+ }, [])
30
+
31
+ return <HeaderThemeContext value={{ headerTheme, setHeaderTheme }}>{children}</HeaderThemeContext>
32
+ }
33
+
34
+ export const useHeaderTheme = (): ContextType => use(HeaderThemeContext)
@@ -0,0 +1,33 @@
1
+ "use client"
2
+
3
+ import { usePathname, useSearchParams } from "next/navigation"
4
+ import posthog from "posthog-js"
5
+ import { Suspense, useEffect } from "react"
6
+
7
+ function PostHogPageviewTracker() {
8
+ const pathname = usePathname()
9
+ const searchParams = useSearchParams()
10
+
11
+ useEffect(() => {
12
+ // Only track in production with PostHog configured
13
+ if (process.env.NODE_ENV !== "production") return
14
+ if (!process.env.NEXT_PUBLIC_POSTHOG_KEY) return
15
+
16
+ // Track pageviews when the route changes
17
+ const url = pathname + (searchParams?.toString() ? `?${searchParams.toString()}` : "")
18
+ posthog.capture("$pageview", { $current_url: url })
19
+ }, [pathname, searchParams])
20
+
21
+ return null
22
+ }
23
+
24
+ export function PostHogProvider({ children }: { children: React.ReactNode }) {
25
+ return (
26
+ <>
27
+ <Suspense fallback={null}>
28
+ <PostHogPageviewTracker />
29
+ </Suspense>
30
+ {children}
31
+ </>
32
+ )
33
+ }
@@ -1,13 +1,14 @@
1
1
  import Script from "next/script"
2
2
  import type React from "react"
3
3
 
4
- import { defaultTheme, themeLocalStorageKey } from "../shared"
4
+ import { defaultTheme, themeLocalStorageKey } from "../ThemeSelector/types"
5
5
 
6
6
  export const InitTheme: React.FC = () => {
7
- return (
8
- <Script
9
- dangerouslySetInnerHTML=\{{
10
- __html: `
7
+ return (
8
+ // eslint-disable-next-line @next/next/no-before-interactive-script-outside-document
9
+ <Script
10
+ dangerouslySetInnerHTML={{
11
+ __html: `
11
12
  (function () {
12
13
  function getSystemPreference() {
13
14
  var mediaQuery = '(prefers-color-scheme: dark)'
@@ -36,9 +37,9 @@ export const InitTheme: React.FC = () => {
36
37
  document.documentElement.setAttribute('data-theme', themeToSet)
37
38
  })();
38
39
  `,
39
- }}
40
- id="theme-script"
41
- strategy="beforeInteractive"
42
- />
43
- )
40
+ }}
41
+ id="theme-script"
42
+ strategy="beforeInteractive"
43
+ />
44
+ )
44
45
  }
@@ -0,0 +1,133 @@
1
+ "use client"
2
+
3
+ import React, { useState } from "react"
4
+
5
+ import type { ThemePreference } from "./types"
6
+
7
+ import { cn } from "@/utilities/ui"
8
+ import { useTheme } from ".."
9
+ import { themeLocalStorageKey } from "./types"
10
+
11
+ // Icons for theme modes
12
+ const SunIcon = () => (
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ width="16"
16
+ height="16"
17
+ viewBox="0 0 24 24"
18
+ fill="none"
19
+ stroke="currentColor"
20
+ strokeWidth="2"
21
+ strokeLinecap="round"
22
+ strokeLinejoin="round"
23
+ aria-hidden="true"
24
+ >
25
+ <title>Light mode</title>
26
+ <circle cx="12" cy="12" r="4" />
27
+ <path d="M12 2v2" />
28
+ <path d="M12 20v2" />
29
+ <path d="m4.93 4.93 1.41 1.41" />
30
+ <path d="m17.66 17.66 1.41 1.41" />
31
+ <path d="M2 12h2" />
32
+ <path d="M20 12h2" />
33
+ <path d="m6.34 17.66-1.41 1.41" />
34
+ <path d="m19.07 4.93-1.41 1.41" />
35
+ </svg>
36
+ )
37
+
38
+ const MoonIcon = () => (
39
+ <svg
40
+ xmlns="http://www.w3.org/2000/svg"
41
+ width="16"
42
+ height="16"
43
+ viewBox="0 0 24 24"
44
+ fill="none"
45
+ stroke="currentColor"
46
+ strokeWidth="2"
47
+ strokeLinecap="round"
48
+ strokeLinejoin="round"
49
+ aria-hidden="true"
50
+ >
51
+ <title>Dark mode</title>
52
+ <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
53
+ </svg>
54
+ )
55
+
56
+ const SystemIcon = () => (
57
+ <svg
58
+ xmlns="http://www.w3.org/2000/svg"
59
+ width="16"
60
+ height="16"
61
+ viewBox="0 0 24 24"
62
+ fill="none"
63
+ stroke="currentColor"
64
+ strokeWidth="2"
65
+ strokeLinecap="round"
66
+ strokeLinejoin="round"
67
+ aria-hidden="true"
68
+ >
69
+ <title>System mode</title>
70
+ <rect width="20" height="14" x="2" y="3" rx="2" />
71
+ <line x1="8" x2="16" y1="21" y2="21" />
72
+ <line x1="12" x2="12" y1="17" y2="21" />
73
+ </svg>
74
+ )
75
+
76
+ export const ThemeSelector: React.FC = () => {
77
+ const { setTheme } = useTheme()
78
+ const [value, setValue] = useState<ThemePreference>("light")
79
+
80
+ const onThemeChange = (themeToSet: ThemePreference) => {
81
+ // Save preference to localStorage
82
+ window.localStorage.setItem(themeLocalStorageKey, themeToSet)
83
+ setValue(themeToSet)
84
+
85
+ if (themeToSet === "auto") {
86
+ // Apply system preference
87
+ setTheme(null)
88
+ } else {
89
+ setTheme(themeToSet)
90
+ }
91
+ }
92
+
93
+ React.useEffect(() => {
94
+ const preference = window.localStorage.getItem(themeLocalStorageKey) as ThemePreference | null
95
+ if (preference === "light" || preference === "dark" || preference === "auto") {
96
+ setValue(preference)
97
+ } else {
98
+ // Default to light if no preference saved
99
+ setValue("light")
100
+ }
101
+ }, [])
102
+
103
+ const themes: { value: ThemePreference; icon: React.ReactNode; label: string }[] = [
104
+ { value: "light", icon: <SunIcon />, label: "Light" },
105
+ { value: "auto", icon: <SystemIcon />, label: "System" },
106
+ { value: "dark", icon: <MoonIcon />, label: "Dark" },
107
+ ]
108
+
109
+ return (
110
+ <div
111
+ className="inline-flex items-center rounded-full border border-border bg-background p-1"
112
+ aria-label="Theme selection"
113
+ >
114
+ {themes.map(({ value: themeValue, icon, label }) => (
115
+ <button
116
+ key={themeValue}
117
+ type="button"
118
+ aria-pressed={value === themeValue}
119
+ aria-label={`${label} theme`}
120
+ onClick={() => onThemeChange(themeValue)}
121
+ className={cn(
122
+ "flex h-7 w-7 items-center justify-center rounded-full transition-all",
123
+ value === themeValue
124
+ ? "bg-primary text-primary-foreground"
125
+ : "text-muted-foreground hover:text-foreground",
126
+ )}
127
+ >
128
+ {icon}
129
+ </button>
130
+ ))}
131
+ </div>
132
+ )
133
+ }
@@ -0,0 +1,7 @@
1
+ export type Theme = "dark" | "light"
2
+
3
+ export type ThemePreference = Theme | "auto"
4
+
5
+ export const themeLocalStorageKey = "payload-theme"
6
+
7
+ export const defaultTheme: Theme = "light"
@@ -0,0 +1,60 @@
1
+ "use client"
2
+
3
+ import type React from "react"
4
+ import { createContext, use, useCallback, useEffect, useState } from "react"
5
+
6
+ import type { Theme, ThemeContextType } from "./types"
7
+
8
+ import canUseDOM from "@/utilities/canUseDOM"
9
+ import { defaultTheme, getImplicitPreference, themeLocalStorageKey } from "./shared"
10
+ import { themeIsValid } from "./types"
11
+
12
+ const initialContext: ThemeContextType = {
13
+ setTheme: () => null,
14
+ theme: undefined,
15
+ }
16
+
17
+ const ThemeContext = createContext(initialContext)
18
+
19
+ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
20
+ const [theme, setThemeState] = useState<Theme | undefined>(
21
+ canUseDOM ? (document.documentElement.getAttribute("data-theme") as Theme) : undefined,
22
+ )
23
+
24
+ const setTheme = useCallback((themeToSet: Theme | null) => {
25
+ if (themeToSet === null) {
26
+ // 'auto' mode - use system preference
27
+ const implicitPreference = getImplicitPreference()
28
+ const resolvedTheme = implicitPreference || defaultTheme
29
+ document.documentElement.setAttribute("data-theme", resolvedTheme)
30
+ setThemeState(resolvedTheme)
31
+ } else {
32
+ setThemeState(themeToSet)
33
+ document.documentElement.setAttribute("data-theme", themeToSet)
34
+ }
35
+ }, [])
36
+
37
+ useEffect(() => {
38
+ let themeToSet: Theme = defaultTheme
39
+ const preference = window.localStorage.getItem(themeLocalStorageKey)
40
+
41
+ if (themeIsValid(preference)) {
42
+ // User explicitly chose light or dark
43
+ themeToSet = preference
44
+ } else if (preference === "auto") {
45
+ // User explicitly chose system preference
46
+ const implicitPreference = getImplicitPreference()
47
+ if (implicitPreference) {
48
+ themeToSet = implicitPreference
49
+ }
50
+ }
51
+ // If no preference, use defaultTheme (light)
52
+
53
+ document.documentElement.setAttribute("data-theme", themeToSet)
54
+ setThemeState(themeToSet)
55
+ }, [])
56
+
57
+ return <ThemeContext value={{ setTheme, theme }}>{children}</ThemeContext>
58
+ }
59
+
60
+ export const useTheme = (): ThemeContextType => use(ThemeContext)
@@ -0,0 +1,17 @@
1
+ import type { Theme } from "./types"
2
+
3
+ export const themeLocalStorageKey = "payload-theme"
4
+
5
+ export const defaultTheme = "light"
6
+
7
+ export const getImplicitPreference = (): Theme | null => {
8
+ const mediaQuery = "(prefers-color-scheme: dark)"
9
+ const mql = window.matchMedia(mediaQuery)
10
+ const hasImplicitPreference = typeof mql.matches === "boolean"
11
+
12
+ if (hasImplicitPreference) {
13
+ return mql.matches ? "dark" : "light"
14
+ }
15
+
16
+ return null
17
+ }
@@ -1,10 +1,10 @@
1
1
  export type Theme = "dark" | "light"
2
2
 
3
3
  export interface ThemeContextType {
4
- setTheme: (theme: Theme | null) => void
5
- theme?: Theme | null
4
+ setTheme: (theme: Theme | null) => void
5
+ theme?: Theme | null
6
6
  }
7
7
 
8
8
  export function themeIsValid(string: null | string): string is Theme {
9
- return string ? ["dark", "light"].includes(string) : false
9
+ return string ? ["dark", "light"].includes(string) : false
10
10
  }
@@ -0,0 +1,17 @@
1
+ import type React from "react"
2
+
3
+ import { HeaderThemeProvider } from "./HeaderTheme"
4
+ import { PostHogProvider } from "./PostHogProvider"
5
+ import { ThemeProvider } from "./Theme"
6
+
7
+ export const Providers: React.FC<{
8
+ children: React.ReactNode
9
+ }> = ({ children }) => {
10
+ return (
11
+ <ThemeProvider>
12
+ <HeaderThemeProvider>
13
+ <PostHogProvider>{children}</PostHogProvider>
14
+ </HeaderThemeProvider>
15
+ </ThemeProvider>
16
+ )
17
+ }
@@ -0,0 +1,42 @@
1
+ "use client"
2
+ import { Input } from "@/components/ui/input"
3
+ import { Label } from "@/components/ui/label"
4
+ import { useDebounce } from "@/utilities/useDebounce"
5
+ import { useRouter } from "next/navigation"
6
+ import type React from "react"
7
+ import { useEffect, useState } from "react"
8
+
9
+ export const Search: React.FC = () => {
10
+ const [value, setValue] = useState("")
11
+ const router = useRouter()
12
+
13
+ const debouncedValue = useDebounce(value)
14
+
15
+ useEffect(() => {
16
+ router.push(`/search${debouncedValue ? `?q=${debouncedValue}` : ""}`)
17
+ }, [debouncedValue, router])
18
+
19
+ return (
20
+ <div>
21
+ <form
22
+ onSubmit={(e) => {
23
+ e.preventDefault()
24
+ }}
25
+ >
26
+ <Label htmlFor="search" className="sr-only">
27
+ Search
28
+ </Label>
29
+ <Input
30
+ id="search"
31
+ onChange={(event) => {
32
+ setValue(event.target.value)
33
+ }}
34
+ placeholder="Search"
35
+ />
36
+ <button type="submit" className="sr-only">
37
+ submit
38
+ </button>
39
+ </form>
40
+ </div>
41
+ )
42
+ }
@@ -0,0 +1,56 @@
1
+ import type { BeforeSync, DocToSync } from "@payloadcms/plugin-search/types"
2
+
3
+ export const beforeSyncWithSearch: BeforeSync = async ({ req, originalDoc, searchDoc }) => {
4
+ const {
5
+ doc: { relationTo: _collection },
6
+ } = searchDoc
7
+
8
+ const { slug, id: _id, categories, title, meta } = originalDoc
9
+
10
+ const modifiedDoc: DocToSync = {
11
+ ...searchDoc,
12
+ slug,
13
+ meta: {
14
+ ...meta,
15
+ title: meta?.title || title,
16
+ image: meta?.image?.id || meta?.image,
17
+ description: meta?.description,
18
+ },
19
+ categories: [],
20
+ }
21
+
22
+ if (categories && Array.isArray(categories) && categories.length > 0) {
23
+ const populatedCategories: { id: string | number; title: string }[] = []
24
+ for (const category of categories) {
25
+ if (!category) {
26
+ continue
27
+ }
28
+
29
+ if (typeof category === "object") {
30
+ populatedCategories.push(category)
31
+ continue
32
+ }
33
+
34
+ const doc = await req.payload.findByID({
35
+ collection: "categories",
36
+ id: category,
37
+ disableErrors: true,
38
+ depth: 0,
39
+ select: { title: true },
40
+ req,
41
+ })
42
+
43
+ if (doc !== null) {
44
+ populatedCategories.push(doc)
45
+ }
46
+ }
47
+
48
+ modifiedDoc.categories = populatedCategories.map((each) => ({
49
+ relationTo: "categories",
50
+ categoryID: String(each.id),
51
+ title: each.title,
52
+ }))
53
+ }
54
+
55
+ return modifiedDoc
56
+ }
@@ -0,0 +1,61 @@
1
+ import type { Field } from "payload"
2
+
3
+ export const searchFields: Field[] = [
4
+ {
5
+ name: "slug",
6
+ type: "text",
7
+ index: true,
8
+ admin: {
9
+ readOnly: true,
10
+ },
11
+ },
12
+ {
13
+ name: "meta",
14
+ label: "Meta",
15
+ type: "group",
16
+ index: true,
17
+ admin: {
18
+ readOnly: true,
19
+ },
20
+ fields: [
21
+ {
22
+ type: "text",
23
+ name: "title",
24
+ label: "Title",
25
+ },
26
+ {
27
+ type: "text",
28
+ name: "description",
29
+ label: "Description",
30
+ },
31
+ {
32
+ name: "image",
33
+ label: "Image",
34
+ type: "upload",
35
+ relationTo: "media",
36
+ },
37
+ ],
38
+ },
39
+ {
40
+ label: "Categories",
41
+ name: "categories",
42
+ type: "array",
43
+ admin: {
44
+ readOnly: true,
45
+ },
46
+ fields: [
47
+ {
48
+ name: "relationTo",
49
+ type: "text",
50
+ },
51
+ {
52
+ name: "categoryID",
53
+ type: "text",
54
+ },
55
+ {
56
+ name: "title",
57
+ type: "text",
58
+ },
59
+ ],
60
+ },
61
+ ]
@@ -0,0 +1,35 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+
4
+ /**
5
+ * Simple object check.
6
+ * @param item
7
+ * @returns {boolean}
8
+ */
9
+ export function isObject(item: unknown): item is object {
10
+ return typeof item === "object" && !Array.isArray(item)
11
+ }
12
+
13
+ /**
14
+ * Deep merge two objects.
15
+ * @param target
16
+ * @param ...sources
17
+ */
18
+ export default function deepMerge<T, R>(target: T, source: R): T {
19
+ const output = { ...target }
20
+ if (isObject(target) && isObject(source)) {
21
+ Object.keys(source).forEach((key) => {
22
+ if (isObject(source[key])) {
23
+ if (!(key in target)) {
24
+ Object.assign(output, { [key]: source[key] })
25
+ } else {
26
+ output[key] = deepMerge(target[key], source[key])
27
+ }
28
+ } else {
29
+ Object.assign(output, { [key]: source[key] })
30
+ }
31
+ })
32
+ }
33
+
34
+ return output
35
+ }
@@ -0,0 +1,78 @@
1
+ import type { DefaultTypedEditorState } from "@payloadcms/richtext-lexical"
2
+
3
+ export interface HeadingItem {
4
+ id: string
5
+ text: string
6
+ depth: number
7
+ }
8
+
9
+ /**
10
+ * Slugify a string for use as an anchor ID
11
+ */
12
+ export function slugify(text: string): string {
13
+ return text
14
+ .toLowerCase()
15
+ .replace(/[^a-z0-9\s-]/g, "")
16
+ .replace(/\s+/g, "-")
17
+ .replace(/-+/g, "-")
18
+ .trim()
19
+ }
20
+
21
+ /**
22
+ * Extract text content from a Lexical node recursively
23
+ */
24
+ function extractTextFromNode(node: Record<string, unknown>): string {
25
+ if (node.type === "text" && typeof node.text === "string") {
26
+ return node.text
27
+ }
28
+
29
+ if (Array.isArray(node.children)) {
30
+ return node.children
31
+ .map((child) => extractTextFromNode(child as Record<string, unknown>))
32
+ .join("")
33
+ }
34
+
35
+ return ""
36
+ }
37
+
38
+ /**
39
+ * Get heading depth from tag name
40
+ */
41
+ function getHeadingDepth(tag: string): number {
42
+ const depthMap: Record<string, number> = {
43
+ h1: 1,
44
+ h2: 2,
45
+ h3: 3,
46
+ h4: 4,
47
+ h5: 5,
48
+ h6: 6,
49
+ }
50
+ return depthMap[tag] || 2
51
+ }
52
+
53
+ /**
54
+ * Extract headings from Lexical editor state for table of contents
55
+ */
56
+ export function extractHeadingsFromLexical(content: DefaultTypedEditorState): HeadingItem[] {
57
+ const headings: HeadingItem[] = []
58
+
59
+ if (!content?.root?.children) {
60
+ return headings
61
+ }
62
+
63
+ for (const node of content.root.children) {
64
+ const nodeRecord = node as Record<string, unknown>
65
+ if (nodeRecord.type === "heading" && typeof nodeRecord.tag === "string") {
66
+ const text = extractTextFromNode(nodeRecord)
67
+ if (text.trim()) {
68
+ headings.push({
69
+ id: slugify(text),
70
+ text: text.trim(),
71
+ depth: getHeadingDepth(nodeRecord.tag),
72
+ })
73
+ }
74
+ }
75
+ }
76
+
77
+ return headings
78
+ }
@@ -0,0 +1,24 @@
1
+ import type { Post } from "@/payload-types"
2
+
3
+ /**
4
+ * Formats an array of populatedAuthors from Posts into a prettified string.
5
+ * @param authors - The populatedAuthors array from a Post.
6
+ * @returns A prettified string of authors.
7
+ * @example
8
+ *
9
+ * [Author1, Author2] becomes 'Author1 and Author2'
10
+ * [Author1, Author2, Author3] becomes 'Author1, Author2, and Author3'
11
+ *
12
+ */
13
+ export const formatAuthors = (
14
+ authors: NonNullable<NonNullable<Post["populatedAuthors"]>[number]>[],
15
+ ) => {
16
+ // Ensure we don't have any authors without a name
17
+ const authorNames = authors.map((author) => author.name).filter(Boolean)
18
+
19
+ if (authorNames.length === 0) return ""
20
+ if (authorNames.length === 1) return authorNames[0]
21
+ if (authorNames.length === 2) return `${authorNames[0]} and ${authorNames[1]}`
22
+
23
+ return `${authorNames.slice(0, -1).join(", ")} and ${authorNames[authorNames.length - 1]}`
24
+ }
@@ -0,0 +1,20 @@
1
+ export const formatDateTime = (timestamp: string): string => {
2
+ const now = new Date()
3
+ let date = now
4
+ if (timestamp) date = new Date(timestamp)
5
+ const months = date.getMonth()
6
+ const days = date.getDate()
7
+ // const hours = date.getHours();
8
+ // const minutes = date.getMinutes();
9
+ // const seconds = date.getSeconds();
10
+
11
+ const MM = months + 1 < 10 ? `0${months + 1}` : months + 1
12
+ const DD = days < 10 ? `0${days}` : days
13
+ const YYYY = date.getFullYear()
14
+ // const AMPM = hours < 12 ? 'AM' : 'PM';
15
+ // const HH = hours > 12 ? hours - 12 : hours;
16
+ // const MinMin = (minutes < 10) ? `0${minutes}` : minutes;
17
+ // const SS = (seconds < 10) ? `0${seconds}` : seconds;
18
+
19
+ return `${MM}/${DD}/${YYYY}`
20
+ }