create-nextblock 0.15.8 → 0.15.9
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/ToasterProvider.tsx +26 -17
- package/templates/nextblock-template/app/actions/contactSellerActions.test.ts +280 -0
- package/templates/nextblock-template/app/actions/contactSellerActions.ts +222 -0
- package/templates/nextblock-template/app/actions/email-retry.test.ts +62 -0
- package/templates/nextblock-template/app/actions/email.ts +241 -110
- package/templates/nextblock-template/app/actions/formActions.ts +245 -116
- package/templates/nextblock-template/app/actions/interactions.ts +489 -396
- package/templates/nextblock-template/app/actions/threadActions.ts +166 -0
- package/templates/nextblock-template/app/api/checkout/route.ts +162 -146
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +664 -1
- package/templates/nextblock-template/app/checkout/page.tsx +57 -52
- package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +552 -529
- package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +304 -181
- package/templates/nextblock-template/app/cms/components/ContactReminderBanner.tsx +75 -0
- package/templates/nextblock-template/app/cms/components/PaymentsReminderBanner.tsx +58 -0
- package/templates/nextblock-template/app/cms/components/VisibilityControl.tsx +542 -528
- package/templates/nextblock-template/app/cms/inquiries/actions.ts +66 -0
- package/templates/nextblock-template/app/cms/inquiries/page.tsx +12 -0
- package/templates/nextblock-template/app/cms/interactions/page.tsx +12 -51
- package/templates/nextblock-template/app/cms/layout.tsx +101 -73
- package/templates/nextblock-template/app/cms/messages/MessagesClient.tsx +661 -0
- package/templates/nextblock-template/app/cms/messages/actions.ts +404 -0
- package/templates/nextblock-template/app/cms/messages/loadInbox.ts +333 -0
- package/templates/nextblock-template/app/cms/messages/page.tsx +87 -0
- package/templates/nextblock-template/app/cms/messages/require-admin.ts +37 -0
- package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +370 -362
- package/templates/nextblock-template/app/cms/revisions/service.ts +20 -0
- package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +227 -185
- package/templates/nextblock-template/app/layout.tsx +671 -671
- package/templates/nextblock-template/app/product/[slug]/page.tsx +502 -482
- package/templates/nextblock-template/app/providers.tsx +96 -96
- package/templates/nextblock-template/app/thread/ThreadView.tsx +164 -0
- package/templates/nextblock-template/app/thread/[token]/route.ts +57 -0
- package/templates/nextblock-template/app/thread/layout.tsx +15 -0
- package/templates/nextblock-template/app/thread/page.tsx +98 -0
- package/templates/nextblock-template/components/BlockRenderer.tsx +312 -296
- package/templates/nextblock-template/components/ContactSellerSection.tsx +188 -0
- package/templates/nextblock-template/components/PostCommentsSection.tsx +378 -369
- package/templates/nextblock-template/components/ProductReviewsSection.tsx +426 -419
- package/templates/nextblock-template/components/StaffReplies.tsx +102 -0
- package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +18 -17
- package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +20 -19
- package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +25 -22
- package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +385 -381
- package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +157 -92
- package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +34 -31
- package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +612 -600
- package/templates/nextblock-template/components/commerce/PaymentReadinessBoundary.tsx +32 -0
- package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +309 -0
- package/templates/nextblock-template/docs/README.md +42 -41
- package/templates/nextblock-template/docs/assets/lighthouse-scores.png +0 -0
- package/templates/nextblock-template/lib/blocks/blockColors.test.ts +22 -2
- package/templates/nextblock-template/lib/blocks/blockColors.ts +44 -1
- package/templates/nextblock-template/lib/blocks/blockRegistry.ts +761 -753
- package/templates/nextblock-template/lib/cms/contact-reminder.ts +64 -0
- package/templates/nextblock-template/lib/cms/payments-reminder.ts +98 -0
- package/templates/nextblock-template/lib/cms/unread-messages.ts +42 -0
- package/templates/nextblock-template/lib/commerce/seller-contact.ts +162 -0
- package/templates/nextblock-template/lib/config/email-settings.ts +323 -254
- package/templates/nextblock-template/lib/config/email-tls.test.ts +57 -0
- package/templates/nextblock-template/lib/email/placeholder-address.test.ts +59 -0
- package/templates/nextblock-template/lib/email/placeholder-address.ts +39 -0
- package/templates/nextblock-template/lib/messages/thread-reference.test.ts +70 -0
- package/templates/nextblock-template/lib/messages/thread-token.test.ts +93 -0
- package/templates/nextblock-template/lib/messages/thread-token.ts +157 -0
- package/templates/nextblock-template/lib/messages/threads.ts +579 -0
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +20 -0
- package/templates/nextblock-template/lib/site-url.test.ts +89 -0
- package/templates/nextblock-template/lib/site-url.ts +102 -48
- package/templates/nextblock-template/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { createClient } from '@nextblock-cms/db';
|
|
5
|
+
import { useTranslations } from '@nextblock-cms/utils';
|
|
6
|
+
import { ShieldCheck } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Staff answers rendered under the review or comment they belong to.
|
|
10
|
+
*
|
|
11
|
+
* A reply is stored as an ordinary approved `type='comment'` row carrying its parent's
|
|
12
|
+
* target, so it is readable through the same public RLS policy the parent is — no
|
|
13
|
+
* special access, nothing new exposed. It is fetched separately rather than joined
|
|
14
|
+
* because the parents arrive one page at a time.
|
|
15
|
+
*
|
|
16
|
+
* Shared by the product-reviews and post-comments sections, which are otherwise near
|
|
17
|
+
* duplicates of each other.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export interface StaffReply {
|
|
21
|
+
id: string;
|
|
22
|
+
content: string;
|
|
23
|
+
created_at: string;
|
|
24
|
+
parent_id: string;
|
|
25
|
+
profiles?: { full_name?: string | null } | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** One query for a whole page of parents, keyed by parent id. */
|
|
29
|
+
export function useStaffReplies(parentIds: string[]): Record<string, StaffReply[]> {
|
|
30
|
+
const [byParent, setByParent] = useState<Record<string, StaffReply[]>>({});
|
|
31
|
+
const key = parentIds.join(',');
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (parentIds.length === 0) {
|
|
35
|
+
setByParent({});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let cancelled = false;
|
|
40
|
+
const supabase = createClient();
|
|
41
|
+
|
|
42
|
+
void supabase
|
|
43
|
+
.from('cms_interactions' as any)
|
|
44
|
+
.select('id, content, created_at, parent_id, profiles(full_name)')
|
|
45
|
+
.in('parent_id', parentIds)
|
|
46
|
+
.eq('status', 'approved')
|
|
47
|
+
.order('created_at', { ascending: true })
|
|
48
|
+
.then(({ data }: { data: StaffReply[] | null }) => {
|
|
49
|
+
if (cancelled || !data) return;
|
|
50
|
+
const grouped: Record<string, StaffReply[]> = {};
|
|
51
|
+
for (const reply of data) {
|
|
52
|
+
(grouped[reply.parent_id] ??= []).push(reply);
|
|
53
|
+
}
|
|
54
|
+
setByParent(grouped);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return () => {
|
|
58
|
+
cancelled = true;
|
|
59
|
+
};
|
|
60
|
+
// Keyed on the joined id list rather than the array: `parentIds` is a fresh array on
|
|
61
|
+
// every render, so depending on it directly would refetch forever.
|
|
62
|
+
}, [key]);
|
|
63
|
+
|
|
64
|
+
return byParent;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function StaffReplies({ replies }: { replies: StaffReply[] | undefined }) {
|
|
68
|
+
const { t, lang } = useTranslations();
|
|
69
|
+
|
|
70
|
+
if (!replies || replies.length === 0) return null;
|
|
71
|
+
|
|
72
|
+
const badge = t('interactions.staff_badge');
|
|
73
|
+
const badgeLabel = badge === 'interactions.staff_badge' ? 'Staff' : badge;
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<ol className="mt-4 space-y-3 border-l-2 border-primary/25 pl-4">
|
|
77
|
+
{replies.map((reply) => (
|
|
78
|
+
<li key={reply.id} className="rounded-xl bg-primary/5 p-3">
|
|
79
|
+
<div className="mb-1 flex flex-wrap items-baseline gap-2">
|
|
80
|
+
<span className="inline-flex items-center gap-1 rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-bold uppercase leading-none text-primary">
|
|
81
|
+
<ShieldCheck className="h-3 w-3" />
|
|
82
|
+
{badgeLabel}
|
|
83
|
+
</span>
|
|
84
|
+
<span className="text-xs font-semibold text-foreground">
|
|
85
|
+
{reply.profiles?.full_name || 'Support'}
|
|
86
|
+
</span>
|
|
87
|
+
<time className="text-[10px] text-muted-foreground" suppressHydrationWarning>
|
|
88
|
+
{new Date(reply.created_at).toLocaleDateString(lang, {
|
|
89
|
+
year: 'numeric',
|
|
90
|
+
month: 'long',
|
|
91
|
+
day: 'numeric',
|
|
92
|
+
})}
|
|
93
|
+
</time>
|
|
94
|
+
</div>
|
|
95
|
+
<p className="whitespace-pre-line text-sm leading-relaxed text-slate-600 dark:text-slate-350">
|
|
96
|
+
{reply.content}
|
|
97
|
+
</p>
|
|
98
|
+
</li>
|
|
99
|
+
))}
|
|
100
|
+
</ol>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Cart } from '@nextblock-cms/ecommerce/components/Cart';
|
|
3
|
+
import PaymentReadinessBoundary from '../../commerce/PaymentReadinessBoundary';
|
|
4
|
+
import type { VisualEditAttributes } from '../../../lib/visual-editing/types';
|
|
5
|
+
|
|
6
|
+
interface CartBlockRendererProps {
|
|
7
|
+
visualEditAttributes?: VisualEditAttributes;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function CartBlockRenderer({ visualEditAttributes }: CartBlockRendererProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div {...visualEditAttributes}>
|
|
13
|
+
<PaymentReadinessBoundary>
|
|
14
|
+
<Cart />
|
|
15
|
+
</PaymentReadinessBoundary>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Checkout } from '@nextblock-cms/ecommerce/components/Checkout';
|
|
3
|
+
import PaymentReadinessBoundary from '../../commerce/PaymentReadinessBoundary';
|
|
4
|
+
import type { VisualEditAttributes } from '../../../lib/visual-editing/types';
|
|
5
|
+
|
|
6
|
+
interface CheckoutBlockRendererProps {
|
|
7
|
+
visualEditAttributes?: VisualEditAttributes;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function CheckoutBlockRenderer({
|
|
11
|
+
visualEditAttributes,
|
|
12
|
+
}: CheckoutBlockRendererProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div {...visualEditAttributes}>
|
|
15
|
+
<PaymentReadinessBoundary>
|
|
16
|
+
<Checkout />
|
|
17
|
+
</PaymentReadinessBoundary>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PaymentReadinessBoundary from '../../commerce/PaymentReadinessBoundary';
|
|
3
|
+
import { FeaturedProductBlock } from '../../../lib/blocks/FeaturedProductBlock';
|
|
4
|
+
import type { FeaturedProductBlockContent } from '../../../lib/blocks/ecommerce-block-schemas';
|
|
5
|
+
import type { VisualEditAttributes } from '../../../lib/visual-editing/types';
|
|
6
|
+
|
|
7
|
+
interface FeaturedProductBlockRendererProps {
|
|
8
|
+
content: FeaturedProductBlockContent;
|
|
9
|
+
languageId: number;
|
|
10
|
+
visualEditAttributes?: VisualEditAttributes;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function FeaturedProductBlockRenderer({
|
|
14
|
+
content,
|
|
15
|
+
visualEditAttributes,
|
|
16
|
+
}: FeaturedProductBlockRendererProps) {
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div {...visualEditAttributes}>
|
|
20
|
+
<PaymentReadinessBoundary>
|
|
21
|
+
<FeaturedProductBlock content={content} />
|
|
22
|
+
</PaymentReadinessBoundary>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|