create-nextblock 0.13.1 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/nextblock-template/app/api/ai/global-agent/route.ts +287 -48
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +238 -209
- package/templates/nextblock-template/app/cms/blocks/components/BackgroundSelector.tsx +103 -8
- package/templates/nextblock-template/app/cms/blocks/components/BlockEditorArea.tsx +31 -2
- package/templates/nextblock-template/app/cms/blocks/components/ColumnEditor.tsx +37 -15
- package/templates/nextblock-template/app/cms/blocks/components/EditableBlock.tsx +26 -15
- package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +123 -46
- package/templates/nextblock-template/app/cms/blocks/editors/SectionBlockEditor.tsx +8 -1
- package/templates/nextblock-template/app/cms/components/CortexGlobalAgentChat.tsx +62 -22
- package/templates/nextblock-template/app/cms/media/import-external-image.ts +289 -0
- package/templates/nextblock-template/app/cms/pages/[id]/edit/EditPageClient.tsx +13 -10
- package/templates/nextblock-template/app/cms/pages/[id]/edit/page.tsx +14 -3
- package/templates/nextblock-template/app/cms/pages/actions.ts +59 -6
- package/templates/nextblock-template/app/cms/posts/[id]/edit/page.tsx +21 -11
- package/templates/nextblock-template/app/cms/posts/actions.ts +45 -0
- package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +11 -9
- package/templates/nextblock-template/app/cms/settings/cortex-ai/StoredCortexAiSettingsClient.tsx +463 -227
- package/templates/nextblock-template/app/cms/settings/cortex-ai/actions.ts +220 -1
- package/templates/nextblock-template/app/cms/settings/cortex-ai/page.tsx +11 -0
- package/templates/nextblock-template/app/lib/homepage.ts +36 -0
- package/templates/nextblock-template/app/lib/sitemap-utils.ts +13 -6
- package/templates/nextblock-template/app/page.tsx +55 -12
- package/templates/nextblock-template/components/blocks/renderers/ImageBlockRenderer.tsx +56 -0
- package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +60 -30
- package/templates/nextblock-template/components/blocks/renderers/StockPhotoCredit.tsx +167 -0
- package/templates/nextblock-template/docs/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +94 -6
- package/templates/nextblock-template/docs/09-LIVE-DRAFT-MODE.md +7 -1
- package/templates/nextblock-template/lib/blocks/blockRegistry.ts +29 -3
- package/templates/nextblock-template/lib/search/server.ts +11 -1
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +82 -72
- package/templates/nextblock-template/next-env.d.ts +1 -1
- package/templates/nextblock-template/package.json +1 -1
- package/templates/nextblock-template/proxy.ts +5 -0
|
@@ -3,13 +3,13 @@ import React from "react";
|
|
|
3
3
|
import { Separator } from "@nextblock-cms/ui";
|
|
4
4
|
import { createClient } from "@nextblock-cms/db/server";
|
|
5
5
|
import PostForm from "../../components/PostForm";
|
|
6
|
-
import { updatePost } from "../../actions";
|
|
6
|
+
import { updatePost, publishPost } from "../../actions";
|
|
7
7
|
import type { Database } from "@nextblock-cms/db";
|
|
8
8
|
import { notFound, redirect } from "next/navigation";
|
|
9
9
|
import BlockEditorArea from "../../../blocks/components/BlockEditorArea";
|
|
10
10
|
import Link from "next/link";
|
|
11
|
-
import { Button } from "@nextblock-cms/ui";
|
|
12
|
-
import { ArrowLeft,
|
|
11
|
+
import { Button, ViewLiveButton } from "@nextblock-cms/ui";
|
|
12
|
+
import { ArrowLeft, FilePenLine } from "lucide-react";
|
|
13
13
|
import ContentLanguageSwitcher from "../../../components/ContentLanguageSwitcher";
|
|
14
14
|
import { getActiveLanguagesServerSide } from "@nextblock-cms/db/server";
|
|
15
15
|
import { normalizeContentDraftRow } from "../../../../../lib/visual-editing/draft-content";
|
|
@@ -30,7 +30,7 @@ interface PostWithBlocks extends PostType {
|
|
|
30
30
|
translation_group_id: string;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
async function getPostDataWithBlocks(id: number): Promise<{ post: PostWithBlocks; hasDraft: boolean } | null> {
|
|
33
|
+
async function getPostDataWithBlocks(id: number): Promise<{ post: PostWithBlocks; hasDraft: boolean; liveStatus: string; liveSlug: string } | null> {
|
|
34
34
|
const supabase = createClient();
|
|
35
35
|
const { data: postData, error: postError } = await supabase
|
|
36
36
|
.from("posts")
|
|
@@ -86,7 +86,14 @@ async function getPostDataWithBlocks(id: number): Promise<{ post: PostWithBlocks
|
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
return {
|
|
89
|
+
return {
|
|
90
|
+
post: postWithBlocks,
|
|
91
|
+
hasDraft,
|
|
92
|
+
// The LIVE row's status/slug (before the draft overlay above) — the "View
|
|
93
|
+
// Live" button must reflect what is actually published, not the draft.
|
|
94
|
+
liveStatus: postData.status as string,
|
|
95
|
+
liveSlug: postData.slug as string,
|
|
96
|
+
};
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
export default async function EditPostPage(props: { params: Promise<{ id: string }> }) {
|
|
@@ -113,7 +120,7 @@ export default async function EditPostPage(props: { params: Promise<{ id: string
|
|
|
113
120
|
return notFound();
|
|
114
121
|
}
|
|
115
122
|
|
|
116
|
-
const { post: postWithBlocks, hasDraft } = postDataResult;
|
|
123
|
+
const { post: postWithBlocks, hasDraft, liveStatus, liveSlug } = postDataResult;
|
|
117
124
|
|
|
118
125
|
let initialFeatureImageUrl: string | null = null;
|
|
119
126
|
let initialFeatureImageIdProp: string | null = null;
|
|
@@ -138,6 +145,8 @@ export default async function EditPostPage(props: { params: Promise<{ id: string
|
|
|
138
145
|
const updatePostWithId = updatePost.bind(null, postId);
|
|
139
146
|
const publicPostUrl = `/article/${postWithBlocks.slug}`;
|
|
140
147
|
const draftModeUrl = `/api/draft/start?path=${encodeURIComponent(publicPostUrl)}`;
|
|
148
|
+
const isLive = liveStatus === "published";
|
|
149
|
+
const liveViewUrl = `/article/${liveSlug}`;
|
|
141
150
|
|
|
142
151
|
return (
|
|
143
152
|
<UploadFolderProvider defaultFolder={`posts/${postWithBlocks.slug}/`}>
|
|
@@ -181,11 +190,12 @@ export default async function EditPostPage(props: { params: Promise<{ id: string
|
|
|
181
190
|
allSiteLanguages={allSiteLanguages}
|
|
182
191
|
/>
|
|
183
192
|
)}
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
193
|
+
<ViewLiveButton
|
|
194
|
+
href={liveViewUrl}
|
|
195
|
+
isLive={isLive}
|
|
196
|
+
label="post"
|
|
197
|
+
publishAction={publishPost.bind(null, postId)}
|
|
198
|
+
/>
|
|
189
199
|
<Button variant="secondary" asChild>
|
|
190
200
|
<a href={draftModeUrl} target="_blank" rel="noopener noreferrer">
|
|
191
201
|
<FilePenLine className="mr-2 h-4 w-4" />
|
|
@@ -12,6 +12,51 @@ type PageStatus = Database['public']['Enums']['page_status'];
|
|
|
12
12
|
import { encodedRedirect } from "@nextblock-cms/utils/server"; // Ensure this is correctly imported
|
|
13
13
|
// --- createPost and updatePost functions to be updated similarly for error returns ---
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Publish a post directly (status -> "published") so it becomes visible on the
|
|
17
|
+
* live site. Used by the draft-aware "View Live" button when an admin chooses to
|
|
18
|
+
* publish a still-draft post.
|
|
19
|
+
*/
|
|
20
|
+
export async function publishPost(postId: number): Promise<{ error?: string } | void> {
|
|
21
|
+
const supabase = createClient();
|
|
22
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
23
|
+
if (!user) return { error: "User not authenticated." };
|
|
24
|
+
|
|
25
|
+
const nowIso = new Date().toISOString();
|
|
26
|
+
const { data: existing } = await supabase
|
|
27
|
+
.from("posts")
|
|
28
|
+
.select("published_at")
|
|
29
|
+
.eq("id", postId)
|
|
30
|
+
.maybeSingle();
|
|
31
|
+
|
|
32
|
+
// Publishing "now": if the post has no publish date or a future-scheduled one,
|
|
33
|
+
// set it to now so it isn't withheld by the public "published_at <= now" filter.
|
|
34
|
+
const currentPublishedAt = existing?.published_at;
|
|
35
|
+
const setPublishedNow =
|
|
36
|
+
!currentPublishedAt || new Date(currentPublishedAt).getTime() > Date.now();
|
|
37
|
+
|
|
38
|
+
const { data: post, error } = await supabase
|
|
39
|
+
.from("posts")
|
|
40
|
+
.update({
|
|
41
|
+
status: "published",
|
|
42
|
+
updated_at: nowIso,
|
|
43
|
+
...(setPublishedNow ? { published_at: nowIso } : {}),
|
|
44
|
+
})
|
|
45
|
+
.eq("id", postId)
|
|
46
|
+
.select("slug")
|
|
47
|
+
.single();
|
|
48
|
+
|
|
49
|
+
if (error || !post) {
|
|
50
|
+
return { error: error?.message || "Could not publish the post." };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
revalidatePath("/cms/posts");
|
|
54
|
+
revalidatePath(`/cms/posts/${postId}/edit`);
|
|
55
|
+
revalidatePath(`/article/${post.slug}`);
|
|
56
|
+
revalidatePath("/articles");
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
|
|
15
60
|
export async function createPost(formData: FormData) {
|
|
16
61
|
const supabase = createClient();
|
|
17
62
|
const { data: { user } } = await supabase.auth.getUser();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { verifyPackageOnline, getActiveLanguagesServerSide } from '@nextblock-cms/db/server';
|
|
2
2
|
import { redirect, notFound } from 'next/navigation';
|
|
3
3
|
import Link from 'next/link';
|
|
4
|
-
import { ArrowLeft, ChevronDown
|
|
4
|
+
import { ArrowLeft, ChevronDown } from 'lucide-react';
|
|
5
5
|
import {
|
|
6
6
|
Badge,
|
|
7
7
|
Button,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
DropdownMenuLabel,
|
|
12
12
|
DropdownMenuSeparator,
|
|
13
13
|
DropdownMenuTrigger,
|
|
14
|
+
ViewLiveButton,
|
|
14
15
|
} from '@nextblock-cms/ui';
|
|
15
16
|
import ProductFormClientShell from '../../ProductFormClientShell';
|
|
16
17
|
import {
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
getStoreConfigStatus,
|
|
22
23
|
normalizeCurrencyRecord,
|
|
23
24
|
updateProductAction,
|
|
25
|
+
publishProductAction,
|
|
24
26
|
getCategoriesWithCount,
|
|
25
27
|
getProductCategories,
|
|
26
28
|
} from '@nextblock-cms/ecommerce/server';
|
|
@@ -265,14 +267,14 @@ export default async function EditProductPage({
|
|
|
265
267
|
</DropdownMenu>
|
|
266
268
|
) : null}
|
|
267
269
|
|
|
268
|
-
{product.slug
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
)}
|
|
270
|
+
{product.slug ? (
|
|
271
|
+
<ViewLiveButton
|
|
272
|
+
href={`/product/${product.slug}`}
|
|
273
|
+
isLive={product.status === 'active'}
|
|
274
|
+
label="product"
|
|
275
|
+
publishAction={publishProductAction.bind(null, product.id)}
|
|
276
|
+
/>
|
|
277
|
+
) : null}
|
|
276
278
|
</div>
|
|
277
279
|
</div>
|
|
278
280
|
|