create-nextblock 0.16.1 → 0.16.3

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 (264) hide show
  1. package/docker-template/.dockerignore +1 -1
  2. package/libs/db/tsconfig.lib.json +3 -3
  3. package/libs/editor/tsconfig.lib.json +3 -3
  4. package/libs/ui/tsconfig.lib.json +3 -3
  5. package/libs/utils/tsconfig.json +3 -3
  6. package/package.json +50 -50
  7. package/project.json +19 -19
  8. package/templates/nextblock-template/.dockerignore +1 -1
  9. package/templates/nextblock-template/.swcrc +30 -30
  10. package/templates/nextblock-template/AGENTS.md +9 -9
  11. package/templates/nextblock-template/CLAUDE.md +1 -1
  12. package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
  13. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
  14. package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -16
  15. package/templates/nextblock-template/app/ToasterProvider.tsx +26 -26
  16. package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
  17. package/templates/nextblock-template/app/actions/contactSellerActions.test.ts +280 -280
  18. package/templates/nextblock-template/app/actions/contactSellerActions.ts +222 -222
  19. package/templates/nextblock-template/app/actions/email-retry.test.ts +62 -62
  20. package/templates/nextblock-template/app/actions/email.ts +241 -241
  21. package/templates/nextblock-template/app/actions/formActions.ts +245 -245
  22. package/templates/nextblock-template/app/actions/interactions.ts +489 -489
  23. package/templates/nextblock-template/app/actions/threadActions.ts +166 -166
  24. package/templates/nextblock-template/app/actions/visibilityActions.ts +210 -210
  25. package/templates/nextblock-template/app/api/ai/seo/alt-text/route.ts +221 -221
  26. package/templates/nextblock-template/app/api/ai/seo/metadata/route.ts +186 -186
  27. package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -29
  28. package/templates/nextblock-template/app/api/checkout/route.ts +162 -162
  29. package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +4 -4
  30. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +4542 -2904
  31. package/templates/nextblock-template/app/api/mcp/route.ts +415 -415
  32. package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
  33. package/templates/nextblock-template/app/api/search/route.ts +43 -43
  34. package/templates/nextblock-template/app/api/view/route.ts +114 -114
  35. package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -47
  36. package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -47
  37. package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
  38. package/templates/nextblock-template/app/cart/page.tsx +7 -7
  39. package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -20
  40. package/templates/nextblock-template/app/checkout/page.tsx +57 -57
  41. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +558 -558
  42. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +241 -241
  43. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
  44. package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +304 -304
  45. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +406 -406
  46. package/templates/nextblock-template/app/cms/components/ContactReminderBanner.tsx +75 -75
  47. package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -23
  48. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
  49. package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +254 -254
  50. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
  51. package/templates/nextblock-template/app/cms/components/PaymentsReminderBanner.tsx +58 -58
  52. package/templates/nextblock-template/app/cms/components/SeoScoreBadge.tsx +40 -0
  53. package/templates/nextblock-template/app/cms/components/TablePagination.tsx +136 -0
  54. package/templates/nextblock-template/app/cms/components/VisibilityBadge.tsx +62 -62
  55. package/templates/nextblock-template/app/cms/components/VisibilityControl.tsx +542 -542
  56. package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -16
  57. package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -16
  58. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
  59. package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -200
  60. package/templates/nextblock-template/app/cms/inquiries/actions.ts +66 -66
  61. package/templates/nextblock-template/app/cms/inquiries/page.tsx +12 -12
  62. package/templates/nextblock-template/app/cms/interactions/page.tsx +12 -12
  63. package/templates/nextblock-template/app/cms/layout.tsx +101 -101
  64. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
  65. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
  66. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
  67. package/templates/nextblock-template/app/cms/messages/MessagesClient.tsx +661 -661
  68. package/templates/nextblock-template/app/cms/messages/actions.ts +404 -404
  69. package/templates/nextblock-template/app/cms/messages/loadInbox.ts +333 -333
  70. package/templates/nextblock-template/app/cms/messages/page.tsx +87 -87
  71. package/templates/nextblock-template/app/cms/messages/require-admin.ts +37 -37
  72. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
  73. package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +649 -649
  74. package/templates/nextblock-template/app/cms/pages/page.tsx +273 -227
  75. package/templates/nextblock-template/app/cms/posts/[id]/edit/page.tsx +2 -0
  76. package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +624 -618
  77. package/templates/nextblock-template/app/cms/posts/page.tsx +251 -194
  78. package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +370 -370
  79. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
  80. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
  81. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
  82. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
  83. package/templates/nextblock-template/app/cms/revisions/actions.ts +332 -332
  84. package/templates/nextblock-template/app/cms/revisions/service.test.ts +498 -498
  85. package/templates/nextblock-template/app/cms/revisions/service.ts +569 -569
  86. package/templates/nextblock-template/app/cms/revisions/utils.ts +304 -304
  87. package/templates/nextblock-template/app/cms/settings/cortex-ai/CortexAiSettingsClient.tsx +948 -948
  88. package/templates/nextblock-template/app/cms/settings/cortex-ai/McpServerSettingsCard.tsx +628 -628
  89. package/templates/nextblock-template/app/cms/settings/cortex-ai/mcp-actions.ts +230 -230
  90. package/templates/nextblock-template/app/cms/settings/cortex-ai/require-admin.ts +34 -34
  91. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
  92. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
  93. package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +227 -227
  94. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
  95. package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +276 -276
  96. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
  97. package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeEditor.tsx +382 -382
  98. package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeManager.tsx +267 -267
  99. package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +40 -40
  100. package/templates/nextblock-template/app/cms/settings/global-css/theme-actions.ts +259 -259
  101. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  102. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
  103. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  104. package/templates/nextblock-template/app/cms/settings/seo/RedirectsCard.tsx +514 -514
  105. package/templates/nextblock-template/app/cms/settings/seo/RobotsCard.tsx +529 -529
  106. package/templates/nextblock-template/app/cms/settings/seo/SeoSettingsClient.tsx +57 -57
  107. package/templates/nextblock-template/app/cms/settings/seo/actions.ts +448 -448
  108. package/templates/nextblock-template/app/cms/settings/seo/mappers.ts +93 -93
  109. package/templates/nextblock-template/app/cms/settings/seo/page.tsx +46 -46
  110. package/templates/nextblock-template/app/cms/settings/seo/require-admin.ts +47 -47
  111. package/templates/nextblock-template/app/cms/settings/site-scripts/page.tsx +51 -51
  112. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
  113. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
  114. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
  115. package/templates/nextblock-template/app/layout.tsx +671 -671
  116. package/templates/nextblock-template/app/lib/seo.ts +319 -319
  117. package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -190
  118. package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -56
  119. package/templates/nextblock-template/app/product/[slug]/page.tsx +502 -502
  120. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
  121. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
  122. package/templates/nextblock-template/app/profile/account-links.ts +22 -22
  123. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
  124. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
  125. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
  126. package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
  127. package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
  128. package/templates/nextblock-template/app/providers.tsx +96 -96
  129. package/templates/nextblock-template/app/robots.ts +123 -123
  130. package/templates/nextblock-template/app/thread/ThreadView.tsx +164 -164
  131. package/templates/nextblock-template/app/thread/[token]/route.ts +57 -57
  132. package/templates/nextblock-template/app/thread/layout.tsx +15 -15
  133. package/templates/nextblock-template/app/thread/page.tsx +98 -98
  134. package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -38
  135. package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -68
  136. package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -35
  137. package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -35
  138. package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -35
  139. package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -34
  140. package/templates/nextblock-template/components/BlockRenderer.tsx +312 -312
  141. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
  142. package/templates/nextblock-template/components/CartTranslator.tsx +210 -210
  143. package/templates/nextblock-template/components/ContactSellerSection.tsx +188 -188
  144. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
  145. package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -51
  146. package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -68
  147. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
  148. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
  149. package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
  150. package/templates/nextblock-template/components/GlobalSearch.tsx +557 -557
  151. package/templates/nextblock-template/components/Header.tsx +38 -38
  152. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
  153. package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
  154. package/templates/nextblock-template/components/PostCommentsSection.tsx +378 -378
  155. package/templates/nextblock-template/components/ProductReviewsSection.tsx +426 -426
  156. package/templates/nextblock-template/components/SiteScripts.tsx +56 -56
  157. package/templates/nextblock-template/components/StaffReplies.tsx +102 -102
  158. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
  159. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
  160. package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +48 -48
  161. package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +9 -9
  162. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
  163. package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -25
  164. package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -92
  165. package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +18 -18
  166. package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +20 -20
  167. package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +25 -25
  168. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +385 -385
  169. package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +24 -24
  170. package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +157 -157
  171. package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +34 -34
  172. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +612 -612
  173. package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -57
  174. package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +2 -2
  175. package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +2 -2
  176. package/templates/nextblock-template/components/blocks/types.ts +7 -7
  177. package/templates/nextblock-template/components/commerce/PaymentReadinessBoundary.tsx +32 -32
  178. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  179. package/templates/nextblock-template/components/form-message.tsx +32 -32
  180. package/templates/nextblock-template/components/seo/GenerateMetaButton.tsx +137 -137
  181. package/templates/nextblock-template/components/seo/PageSeoAuditSection.tsx +251 -244
  182. package/templates/nextblock-template/components/seo/SeoAuditPanel.tsx +749 -749
  183. package/templates/nextblock-template/components/seo/SeoIssueList.tsx +195 -195
  184. package/templates/nextblock-template/components/seo/SeoScoreDial.tsx +144 -144
  185. package/templates/nextblock-template/components/seo/SocialPreview.tsx +243 -243
  186. package/templates/nextblock-template/components/seo/SocialPreviewDialog.tsx +110 -110
  187. package/templates/nextblock-template/components/submit-button.tsx +23 -23
  188. package/templates/nextblock-template/components/theme-icon.tsx +78 -78
  189. package/templates/nextblock-template/components/theme-switcher.tsx +85 -85
  190. package/templates/nextblock-template/context/AuthContext.tsx +23 -23
  191. package/templates/nextblock-template/context/ThemeCatalogContext.tsx +44 -44
  192. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
  193. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +77 -77
  194. package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +372 -372
  195. package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +309 -309
  196. package/templates/nextblock-template/docs/README.md +42 -42
  197. package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12506 -12506
  198. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
  199. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
  200. package/templates/nextblock-template/index.d.ts +7 -7
  201. package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
  202. package/templates/nextblock-template/lib/blocks/blockColors.test.ts +134 -134
  203. package/templates/nextblock-template/lib/blocks/blockColors.ts +175 -175
  204. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +761 -761
  205. package/templates/nextblock-template/lib/blocks/inlineScriptNonce.ts +20 -20
  206. package/templates/nextblock-template/lib/cms/contact-reminder.ts +64 -64
  207. package/templates/nextblock-template/lib/cms/payments-reminder.test.ts +135 -0
  208. package/templates/nextblock-template/lib/cms/payments-reminder.ts +30 -23
  209. package/templates/nextblock-template/lib/cms/unread-messages.ts +42 -42
  210. package/templates/nextblock-template/lib/commerce/seller-contact.ts +162 -162
  211. package/templates/nextblock-template/lib/config/email-settings.ts +323 -323
  212. package/templates/nextblock-template/lib/config/email-tls.test.ts +57 -57
  213. package/templates/nextblock-template/lib/cortex-ai/alt-text-request.ts +86 -86
  214. package/templates/nextblock-template/lib/cortex-ai/sandbox-headers.ts +60 -60
  215. package/templates/nextblock-template/lib/email/placeholder-address.test.ts +59 -59
  216. package/templates/nextblock-template/lib/email/placeholder-address.ts +39 -39
  217. package/templates/nextblock-template/lib/messages/thread-reference.test.ts +70 -70
  218. package/templates/nextblock-template/lib/messages/thread-token.test.ts +93 -93
  219. package/templates/nextblock-template/lib/messages/thread-token.ts +157 -157
  220. package/templates/nextblock-template/lib/messages/threads.ts +579 -579
  221. package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
  222. package/templates/nextblock-template/lib/publishing/viewUrl.ts +26 -26
  223. package/templates/nextblock-template/lib/search/types.ts +27 -27
  224. package/templates/nextblock-template/lib/seo/alt-text-write-back.test.ts +154 -154
  225. package/templates/nextblock-template/lib/seo/alt-text-write-back.ts +109 -109
  226. package/templates/nextblock-template/lib/seo/block-content.ts +123 -123
  227. package/templates/nextblock-template/lib/seo/fix-prompts.test.ts +242 -242
  228. package/templates/nextblock-template/lib/seo/fix-prompts.ts +204 -204
  229. package/templates/nextblock-template/lib/seo/page-audit-context.tsx +157 -140
  230. package/templates/nextblock-template/lib/seo/page-document.test.ts +384 -350
  231. package/templates/nextblock-template/lib/seo/page-document.ts +463 -412
  232. package/templates/nextblock-template/lib/seo/redirect-store.test.ts +479 -479
  233. package/templates/nextblock-template/lib/seo/redirect-store.ts +466 -466
  234. package/templates/nextblock-template/lib/seo/robots-settings-signature.test.ts +102 -102
  235. package/templates/nextblock-template/lib/seo/robots-settings-signature.ts +41 -41
  236. package/templates/nextblock-template/lib/seo/robots-txt.test.ts +370 -370
  237. package/templates/nextblock-template/lib/seo/robots-txt.ts +510 -510
  238. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +37 -22
  239. package/templates/nextblock-template/lib/site-scripts/revisions.ts +71 -71
  240. package/templates/nextblock-template/lib/site-scripts/types.ts +46 -46
  241. package/templates/nextblock-template/lib/site-url.test.ts +89 -89
  242. package/templates/nextblock-template/lib/site-url.ts +102 -102
  243. package/templates/nextblock-template/lib/themes/buildThemeCss.ts +124 -124
  244. package/templates/nextblock-template/lib/themes/tokenColor.ts +31 -31
  245. package/templates/nextblock-template/lib/themes/tokens.ts +143 -143
  246. package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -105
  247. package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -42
  248. package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -143
  249. package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -94
  250. package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -81
  251. package/templates/nextblock-template/lib/zod-config.ts +5 -5
  252. package/templates/nextblock-template/next-env.d.ts +1 -2
  253. package/templates/nextblock-template/package.json +1 -1
  254. package/templates/nextblock-template/postcss.config.js +6 -6
  255. package/templates/nextblock-template/scripts/backup.js +115 -115
  256. package/templates/nextblock-template/scripts/restore.js +385 -385
  257. package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -112
  258. package/templates/nextblock-template/tailwind.config.js +25 -25
  259. package/templates/nextblock-template/tools/build-migrate.mjs +102 -102
  260. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
  261. package/templates/nextblock-template/tools/deploy-supabase.js +159 -159
  262. package/templates/nextblock-template/tools/lib/migrate-core.mjs +569 -569
  263. package/templates/nextblock-template/tools/update.mjs +1303 -1303
  264. package/tsconfig.base.json +3 -3
@@ -1,227 +1,273 @@
1
- // app/cms/pages/page.tsx
2
- import React from "react";
3
- import { createClient } from "@nextblock-cms/db/server";
4
- import Link from "next/link";
5
- import { Button } from "@nextblock-cms/ui";
6
- import {
7
- Table,
8
- TableBody,
9
- TableCell,
10
- TableHead,
11
- TableHeader,
12
- TableRow,
13
- } from "@nextblock-cms/ui";
14
- import { Badge } from "@nextblock-cms/ui";
15
- import { Alert, AlertDescription } from "@nextblock-cms/ui";
16
- import {
17
- MoreHorizontal,
18
- PlusCircle,
19
- Edit3,
20
- FileText,
21
- } from "lucide-react"; // Trash2 removed from here
22
- import {
23
- DropdownMenu,
24
- DropdownMenuContent,
25
- DropdownMenuItem,
26
- DropdownMenuButtonTrigger,
27
- DropdownMenuSeparator,
28
- } from "@nextblock-cms/ui";
29
- // Server action `deletePage` is used by DeletePageButtonClient
30
- import type { Database } from "@nextblock-cms/db";
31
- import { getActiveLanguagesServerSide } from "@nextblock-cms/db/server";
32
-
33
- type Page = Database["public"]["Tables"]["pages"]["Row"];
34
- import LanguageFilterSelect from "../components/LanguageFilterSelect";
35
- import DeletePageButtonClient from "./components/DeletePageButtonClient"; // Import the client component
36
- import { ContentTransferControls } from "../import-export/ContentTransferControls";
37
- import VisibilityBadge from "../components/VisibilityBadge";
38
-
39
- async function getPagesWithDetails(
40
- filterLanguageId?: number
41
- ): Promise<{ page: Page; languageCode: string }[]> {
42
- const supabase = createClient();
43
- const languages = await getActiveLanguagesServerSide();
44
- const langMap = new Map(languages.map((l) => [l.id, l.code]));
45
-
46
- let query = supabase
47
- .from("pages")
48
- .select("*, languages!inner(code)")
49
- .order("created_at", { ascending: false });
50
-
51
- if (filterLanguageId) {
52
- query = query.eq("language_id", filterLanguageId);
53
- }
54
-
55
- const { data: pagesData, error } = await query;
56
-
57
- if (error) {
58
- console.error("Error fetching pages:", error);
59
- return [];
60
- }
61
- if (!pagesData) return [];
62
-
63
- return pagesData.map((p) => {
64
- const langInfo = p.languages as unknown as { code: string } | null;
65
- return {
66
- page: p as Page,
67
- languageCode:
68
- langInfo?.code?.toUpperCase() ||
69
- langMap.get(p.language_id)?.toUpperCase() ||
70
- "N/A",
71
- };
72
- });
73
- }
74
-
75
- interface CmsPagesListPageProps {
76
- searchParams?: Promise<{
77
- lang?: string;
78
- success?: string;
79
- }>;
80
- }
81
-
82
- export default async function CmsPagesListPage(props: CmsPagesListPageProps) {
83
- const searchParams = await props.searchParams;
84
- const allLanguages = await getActiveLanguagesServerSide();
85
- const selectedLangId = searchParams?.lang
86
- ? parseInt(searchParams.lang, 10)
87
- : undefined;
88
-
89
- const isValidLangId = selectedLangId
90
- ? allLanguages.some((l) => l.id === selectedLangId)
91
- : true;
92
- const filterLangId = isValidLangId ? selectedLangId : undefined;
93
-
94
- const pagesWithDetails = await getPagesWithDetails(filterLangId);
95
- const successMessage = searchParams?.success;
96
-
97
- return (
98
- <div className="w-full">
99
- <div className="flex justify-between items-center mb-6 flex-wrap gap-4">
100
- <h1 className="text-2xl font-semibold">Manage Pages</h1>
101
- <div className="flex items-center gap-3">
102
- <ContentTransferControls
103
- contentType="pages"
104
- label="Pages"
105
- languageId={filterLangId}
106
- hasContent={pagesWithDetails.length > 0}
107
- />
108
- <LanguageFilterSelect
109
- allLanguages={allLanguages}
110
- currentFilterLangId={filterLangId}
111
- basePath="/cms/pages"
112
- />
113
- <Button variant="default" asChild>
114
- <Link href="/cms/pages/new">
115
- <PlusCircle className="mr-2 h-4 w-4" /> Create New Page
116
- </Link>
117
- </Button>
118
- </div>
119
- </div>
120
-
121
- {successMessage && (
122
- <Alert variant="success" className="mb-4">
123
- <AlertDescription>
124
- {decodeURIComponent(successMessage)}
125
- </AlertDescription>
126
- </Alert>
127
- )}
128
-
129
- {pagesWithDetails.length === 0 ? (
130
- <div className="text-center py-10 border rounded-lg dark:border-slate-700">
131
- <FileText className="mx-auto h-12 w-12 text-muted-foreground" />
132
- <h3 className="mt-2 text-sm font-medium text-foreground">
133
- {filterLangId
134
- ? "No pages found for the selected language."
135
- : "No pages found."}
136
- </h3>
137
- <p className="mt-1 text-sm text-muted-foreground">
138
- Get started by creating a new page.
139
- </p>
140
- <div className="mt-6">
141
- <Button asChild>
142
- <Link href="/cms/pages/new">
143
- <PlusCircle className="mr-2 h-4 w-4" /> Create Page
144
- </Link>
145
- </Button>
146
- </div>
147
- </div>
148
- ) : (
149
- <div className="rounded-lg border overflow-hidden dark:border-slate-700">
150
- <Table>
151
- <TableHeader>
152
- <TableRow className="dark:border-slate-700">
153
- <TableHead className="w-[300px] sm:w-[400px]">Title</TableHead>
154
- <TableHead>Status</TableHead>
155
- <TableHead>Language</TableHead>
156
- <TableHead className="hidden md:table-cell">Slug</TableHead>
157
- <TableHead className="hidden lg:table-cell">
158
- Last Updated
159
- </TableHead>
160
- <TableHead className="text-right w-[80px]">Actions</TableHead>
161
- </TableRow>
162
- </TableHeader>
163
- <TableBody>
164
- {pagesWithDetails.map(({ page, languageCode }) => (
165
- <TableRow key={page.id} className="dark:border-slate-700">
166
- <TableCell className="font-medium">
167
- <Link
168
- href={`/cms/pages/${page.id}/edit`}
169
- className="flex items-center cursor-pointer"
170
- >
171
- <Edit3 className="mr-2 h-4 w-4" />
172
- {page.title}
173
- </Link>
174
- </TableCell>
175
- <TableCell>
176
- <VisibilityBadge
177
- type="page"
178
- status={page.status}
179
- publishedAt={page.published_at}
180
- />
181
- </TableCell>
182
- <TableCell>
183
- <Badge variant="outline" className="dark:border-slate-600">
184
- {languageCode}
185
- </Badge>
186
- </TableCell>
187
- <TableCell className="text-muted-foreground text-xs hidden md:table-cell">
188
- /{page.slug}
189
- </TableCell>
190
- <TableCell className="hidden lg:table-cell text-xs text-muted-foreground">
191
- {new Date(page.updated_at).toLocaleDateString()}
192
- </TableCell>
193
- <TableCell className="text-right">
194
- <DropdownMenu>
195
- <DropdownMenuButtonTrigger id={`page-trigger-${page.id}`}>
196
- <MoreHorizontal className="h-4 w-4" />
197
- <span className="sr-only">
198
- Page actions for {page.title}
199
- </span>
200
- </DropdownMenuButtonTrigger>
201
- <DropdownMenuContent align="end">
202
- <DropdownMenuItem asChild>
203
- <Link
204
- href={`/cms/pages/${page.id}/edit`}
205
- className="flex items-center cursor-pointer"
206
- >
207
- <Edit3 className="mr-2 h-4 w-4" /> Edit
208
- </Link>
209
- </DropdownMenuItem>
210
- <DropdownMenuSeparator />
211
- {/* Use the Client Component for the delete button */}
212
- <DeletePageButtonClient
213
- pageId={page.id}
214
- pageTitle={page.title}
215
- />
216
- </DropdownMenuContent>
217
- </DropdownMenu>
218
- </TableCell>
219
- </TableRow>
220
- ))}
221
- </TableBody>
222
- </Table>
223
- </div>
224
- )}
225
- </div>
226
- );
227
- }
1
+ // app/cms/pages/page.tsx
2
+ import React from "react";
3
+ import { createClient } from "@nextblock-cms/db/server";
4
+ import Link from "next/link";
5
+ import { Button } from "@nextblock-cms/ui";
6
+ import {
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ } from "@nextblock-cms/ui";
14
+ import { Badge } from "@nextblock-cms/ui";
15
+ import { Alert, AlertDescription } from "@nextblock-cms/ui";
16
+ import {
17
+ MoreHorizontal,
18
+ PlusCircle,
19
+ Edit3,
20
+ FileText,
21
+ } from "lucide-react";
22
+ import {
23
+ DropdownMenu,
24
+ DropdownMenuContent,
25
+ DropdownMenuItem,
26
+ DropdownMenuButtonTrigger,
27
+ DropdownMenuSeparator,
28
+ } from "@nextblock-cms/ui";
29
+ import type { Database } from "@nextblock-cms/db";
30
+ import { getActiveLanguagesServerSide } from "@nextblock-cms/db/server";
31
+ import { auditSeo } from "@nextblock-cms/utils/seo";
32
+ import { buildPageSeoDocument } from "../../../lib/seo/page-document";
33
+
34
+ type Page = Database["public"]["Tables"]["pages"]["Row"];
35
+ import LanguageFilterSelect from "../components/LanguageFilterSelect";
36
+ import DeletePageButtonClient from "./components/DeletePageButtonClient";
37
+ import { ContentTransferControls } from "../import-export/ContentTransferControls";
38
+ import VisibilityBadge from "../components/VisibilityBadge";
39
+ import SeoScoreBadge from "../components/SeoScoreBadge";
40
+ import TablePagination from "../components/TablePagination";
41
+
42
+ async function getPagesWithDetails(
43
+ filterLanguageId?: number,
44
+ pageNumber: number = 1,
45
+ pageSize: number = 25
46
+ ): Promise<{
47
+ items: { page: Page; languageCode: string; seoScore: number }[];
48
+ totalCount: number;
49
+ }> {
50
+ const supabase = createClient();
51
+ const languages = await getActiveLanguagesServerSide();
52
+ const langMap = new Map(languages.map((l) => [l.id, l.code]));
53
+
54
+ let query = supabase
55
+ .from("pages")
56
+ .select("*, languages!inner(code), blocks(id, block_type, content, order)", { count: "exact" })
57
+ .order("created_at", { ascending: false });
58
+
59
+ if (filterLanguageId) {
60
+ query = query.eq("language_id", filterLanguageId);
61
+ }
62
+
63
+ const from = (pageNumber - 1) * pageSize;
64
+ const to = from + pageSize - 1;
65
+
66
+ const { data: pagesData, count, error } = await query.range(from, to);
67
+
68
+ if (error) {
69
+ console.error("Error fetching pages:", error);
70
+ return { items: [], totalCount: 0 };
71
+ }
72
+ if (!pagesData) return { items: [], totalCount: 0 };
73
+
74
+ const items = pagesData.map((p) => {
75
+ const langInfo = p.languages as unknown as { code: string } | null;
76
+ const rawBlocks = (p.blocks || []) as unknown as Parameters<typeof buildPageSeoDocument>[0];
77
+ const doc = buildPageSeoDocument(rawBlocks);
78
+ const auditResult = auditSeo({
79
+ document: doc,
80
+ metaTitle: p.meta_title ?? undefined,
81
+ metaDescription: p.meta_description ?? undefined,
82
+ scope: "page",
83
+ });
84
+
85
+ const langCode = langInfo?.code || langMap.get(p.language_id) || "N/A";
86
+
87
+ return {
88
+ page: p as unknown as Page,
89
+ languageCode: String(langCode).toUpperCase(),
90
+ seoScore: auditResult.score,
91
+ };
92
+ });
93
+
94
+ return { items, totalCount: count ?? items.length };
95
+ }
96
+
97
+ interface CmsPagesListPageProps {
98
+ searchParams?: Promise<{
99
+ lang?: string;
100
+ success?: string;
101
+ page?: string;
102
+ pageSize?: string;
103
+ }>;
104
+ }
105
+
106
+ export default async function CmsPagesListPage(props: CmsPagesListPageProps) {
107
+ const searchParams = await props.searchParams;
108
+ const allLanguages = await getActiveLanguagesServerSide();
109
+ const selectedLangId = searchParams?.lang
110
+ ? parseInt(searchParams.lang, 10)
111
+ : undefined;
112
+
113
+ const isValidLangId = selectedLangId
114
+ ? allLanguages.some((l) => l.id === selectedLangId)
115
+ : true;
116
+ const filterLangId = isValidLangId ? selectedLangId : undefined;
117
+
118
+ const pageNumber = searchParams?.page
119
+ ? Math.max(1, parseInt(searchParams.page, 10) || 1)
120
+ : 1;
121
+ const pageSize = searchParams?.pageSize
122
+ ? Math.max(1, parseInt(searchParams.pageSize, 10) || 25)
123
+ : 25;
124
+
125
+ const { items: pagesWithDetails, totalCount } = await getPagesWithDetails(
126
+ filterLangId,
127
+ pageNumber,
128
+ pageSize
129
+ );
130
+ const successMessage = searchParams?.success;
131
+
132
+ return (
133
+ <div className="w-full">
134
+ <div className="flex justify-between items-center mb-6 flex-wrap gap-4">
135
+ <h1 className="text-2xl font-semibold">Manage Pages</h1>
136
+ <div className="flex items-center gap-3">
137
+ <ContentTransferControls
138
+ contentType="pages"
139
+ label="Pages"
140
+ languageId={filterLangId}
141
+ hasContent={pagesWithDetails.length > 0}
142
+ />
143
+ <LanguageFilterSelect
144
+ allLanguages={allLanguages}
145
+ currentFilterLangId={filterLangId}
146
+ basePath="/cms/pages"
147
+ />
148
+ <Button variant="default" asChild>
149
+ <Link href="/cms/pages/new">
150
+ <PlusCircle className="mr-2 h-4 w-4" /> Create New Page
151
+ </Link>
152
+ </Button>
153
+ </div>
154
+ </div>
155
+
156
+ {successMessage && (
157
+ <Alert variant="success" className="mb-4">
158
+ <AlertDescription>
159
+ {decodeURIComponent(successMessage)}
160
+ </AlertDescription>
161
+ </Alert>
162
+ )}
163
+
164
+ {totalCount === 0 ? (
165
+ <div className="text-center py-10 border rounded-lg dark:border-slate-700">
166
+ <FileText className="mx-auto h-12 w-12 text-muted-foreground" />
167
+ <h3 className="mt-2 text-sm font-medium text-foreground">
168
+ {filterLangId
169
+ ? "No pages found for the selected language."
170
+ : "No pages found."}
171
+ </h3>
172
+ <p className="mt-1 text-sm text-muted-foreground">
173
+ Get started by creating a new page.
174
+ </p>
175
+ <div className="mt-6">
176
+ <Button asChild>
177
+ <Link href="/cms/pages/new">
178
+ <PlusCircle className="mr-2 h-4 w-4" /> Create Page
179
+ </Link>
180
+ </Button>
181
+ </div>
182
+ </div>
183
+ ) : (
184
+ <div className="rounded-lg border overflow-hidden dark:border-slate-700">
185
+ <Table>
186
+ <TableHeader>
187
+ <TableRow className="dark:border-slate-700">
188
+ <TableHead className="w-[280px] sm:w-[350px]">Title</TableHead>
189
+ <TableHead>Status</TableHead>
190
+ <TableHead>Language</TableHead>
191
+ <TableHead className="hidden md:table-cell">Slug</TableHead>
192
+ <TableHead>SEO</TableHead>
193
+ <TableHead className="hidden lg:table-cell">
194
+ Last Updated
195
+ </TableHead>
196
+ <TableHead className="text-right w-[80px]">Actions</TableHead>
197
+ </TableRow>
198
+ </TableHeader>
199
+ <TableBody>
200
+ {pagesWithDetails.map(({ page, languageCode, seoScore }) => (
201
+ <TableRow key={page.id} className="dark:border-slate-700">
202
+ <TableCell className="font-medium">
203
+ <Link
204
+ href={`/cms/pages/${page.id}/edit`}
205
+ className="flex items-center cursor-pointer"
206
+ >
207
+ <Edit3 className="mr-2 h-4 w-4" />
208
+ {page.title}
209
+ </Link>
210
+ </TableCell>
211
+ <TableCell>
212
+ <VisibilityBadge
213
+ type="page"
214
+ status={page.status}
215
+ publishedAt={page.published_at}
216
+ />
217
+ </TableCell>
218
+ <TableCell>
219
+ <Badge variant="outline" className="dark:border-slate-600">
220
+ {languageCode}
221
+ </Badge>
222
+ </TableCell>
223
+ <TableCell className="text-muted-foreground text-xs hidden md:table-cell">
224
+ /{page.slug}
225
+ </TableCell>
226
+ <TableCell>
227
+ <SeoScoreBadge score={seoScore} />
228
+ </TableCell>
229
+ <TableCell className="hidden lg:table-cell text-xs text-muted-foreground">
230
+ {new Date(page.updated_at).toLocaleDateString()}
231
+ </TableCell>
232
+ <TableCell className="text-right">
233
+ <DropdownMenu>
234
+ <DropdownMenuButtonTrigger id={`page-trigger-${page.id}`}>
235
+ <MoreHorizontal className="h-4 w-4" />
236
+ <span className="sr-only">
237
+ Page actions for {page.title}
238
+ </span>
239
+ </DropdownMenuButtonTrigger>
240
+ <DropdownMenuContent align="end">
241
+ <DropdownMenuItem asChild>
242
+ <Link
243
+ href={`/cms/pages/${page.id}/edit`}
244
+ className="flex items-center cursor-pointer"
245
+ >
246
+ <Edit3 className="mr-2 h-4 w-4" /> Edit
247
+ </Link>
248
+ </DropdownMenuItem>
249
+ <DropdownMenuSeparator />
250
+ <DeletePageButtonClient
251
+ pageId={page.id}
252
+ pageTitle={page.title}
253
+ />
254
+ </DropdownMenuContent>
255
+ </DropdownMenu>
256
+ </TableCell>
257
+ </TableRow>
258
+ ))}
259
+ </TableBody>
260
+ </Table>
261
+
262
+ <TablePagination
263
+ currentPage={pageNumber}
264
+ pageSize={pageSize}
265
+ totalCount={totalCount}
266
+ basePath="/cms/pages"
267
+ itemLabel="pages"
268
+ />
269
+ </div>
270
+ )}
271
+ </div>
272
+ );
273
+ }
@@ -189,6 +189,8 @@ export default async function EditPostPage(props: { params: Promise<{ id: string
189
189
  paint instead of an empty document.
190
190
  */}
191
191
  <PageSeoProvider
192
+ documentTitle={postWithBlocks.title}
193
+ documentType="post"
192
194
  initialBlocks={postWithBlocks.blocks}
193
195
  initialMetaDescription={postWithBlocks.meta_description}
194
196
  initialMetaTitle={postWithBlocks.meta_title}