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.
Files changed (71) hide show
  1. package/package.json +1 -1
  2. package/templates/nextblock-template/app/ToasterProvider.tsx +26 -17
  3. package/templates/nextblock-template/app/actions/contactSellerActions.test.ts +280 -0
  4. package/templates/nextblock-template/app/actions/contactSellerActions.ts +222 -0
  5. package/templates/nextblock-template/app/actions/email-retry.test.ts +62 -0
  6. package/templates/nextblock-template/app/actions/email.ts +241 -110
  7. package/templates/nextblock-template/app/actions/formActions.ts +245 -116
  8. package/templates/nextblock-template/app/actions/interactions.ts +489 -396
  9. package/templates/nextblock-template/app/actions/threadActions.ts +166 -0
  10. package/templates/nextblock-template/app/api/checkout/route.ts +162 -146
  11. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +664 -1
  12. package/templates/nextblock-template/app/checkout/page.tsx +57 -52
  13. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +552 -529
  14. package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +304 -181
  15. package/templates/nextblock-template/app/cms/components/ContactReminderBanner.tsx +75 -0
  16. package/templates/nextblock-template/app/cms/components/PaymentsReminderBanner.tsx +58 -0
  17. package/templates/nextblock-template/app/cms/components/VisibilityControl.tsx +542 -528
  18. package/templates/nextblock-template/app/cms/inquiries/actions.ts +66 -0
  19. package/templates/nextblock-template/app/cms/inquiries/page.tsx +12 -0
  20. package/templates/nextblock-template/app/cms/interactions/page.tsx +12 -51
  21. package/templates/nextblock-template/app/cms/layout.tsx +101 -73
  22. package/templates/nextblock-template/app/cms/messages/MessagesClient.tsx +661 -0
  23. package/templates/nextblock-template/app/cms/messages/actions.ts +404 -0
  24. package/templates/nextblock-template/app/cms/messages/loadInbox.ts +333 -0
  25. package/templates/nextblock-template/app/cms/messages/page.tsx +87 -0
  26. package/templates/nextblock-template/app/cms/messages/require-admin.ts +37 -0
  27. package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +370 -362
  28. package/templates/nextblock-template/app/cms/revisions/service.ts +20 -0
  29. package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +227 -185
  30. package/templates/nextblock-template/app/layout.tsx +671 -671
  31. package/templates/nextblock-template/app/product/[slug]/page.tsx +502 -482
  32. package/templates/nextblock-template/app/providers.tsx +96 -96
  33. package/templates/nextblock-template/app/thread/ThreadView.tsx +164 -0
  34. package/templates/nextblock-template/app/thread/[token]/route.ts +57 -0
  35. package/templates/nextblock-template/app/thread/layout.tsx +15 -0
  36. package/templates/nextblock-template/app/thread/page.tsx +98 -0
  37. package/templates/nextblock-template/components/BlockRenderer.tsx +312 -296
  38. package/templates/nextblock-template/components/ContactSellerSection.tsx +188 -0
  39. package/templates/nextblock-template/components/PostCommentsSection.tsx +378 -369
  40. package/templates/nextblock-template/components/ProductReviewsSection.tsx +426 -419
  41. package/templates/nextblock-template/components/StaffReplies.tsx +102 -0
  42. package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +18 -17
  43. package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +20 -19
  44. package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +25 -22
  45. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +385 -381
  46. package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +157 -92
  47. package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +34 -31
  48. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +612 -600
  49. package/templates/nextblock-template/components/commerce/PaymentReadinessBoundary.tsx +32 -0
  50. package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +309 -0
  51. package/templates/nextblock-template/docs/README.md +42 -41
  52. package/templates/nextblock-template/docs/assets/lighthouse-scores.png +0 -0
  53. package/templates/nextblock-template/lib/blocks/blockColors.test.ts +22 -2
  54. package/templates/nextblock-template/lib/blocks/blockColors.ts +44 -1
  55. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +761 -753
  56. package/templates/nextblock-template/lib/cms/contact-reminder.ts +64 -0
  57. package/templates/nextblock-template/lib/cms/payments-reminder.ts +98 -0
  58. package/templates/nextblock-template/lib/cms/unread-messages.ts +42 -0
  59. package/templates/nextblock-template/lib/commerce/seller-contact.ts +162 -0
  60. package/templates/nextblock-template/lib/config/email-settings.ts +323 -254
  61. package/templates/nextblock-template/lib/config/email-tls.test.ts +57 -0
  62. package/templates/nextblock-template/lib/email/placeholder-address.test.ts +59 -0
  63. package/templates/nextblock-template/lib/email/placeholder-address.ts +39 -0
  64. package/templates/nextblock-template/lib/messages/thread-reference.test.ts +70 -0
  65. package/templates/nextblock-template/lib/messages/thread-token.test.ts +93 -0
  66. package/templates/nextblock-template/lib/messages/thread-token.ts +157 -0
  67. package/templates/nextblock-template/lib/messages/threads.ts +579 -0
  68. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +20 -0
  69. package/templates/nextblock-template/lib/site-url.test.ts +89 -0
  70. package/templates/nextblock-template/lib/site-url.ts +102 -48
  71. 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
- 'use client';
2
-
3
- import React from 'react';
4
- import { Cart } from '@nextblock-cms/ecommerce/components/Cart';
5
- import type { VisualEditAttributes } from '../../../lib/visual-editing/types';
6
-
7
- interface CartBlockRendererProps {
8
- visualEditAttributes?: VisualEditAttributes;
9
- }
10
-
11
- export default function CartBlockRenderer({ visualEditAttributes }: CartBlockRendererProps) {
12
- return (
13
- <div {...visualEditAttributes}>
14
- <Cart />
15
- </div>
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
- 'use client';
2
-
3
- import React from 'react';
4
- import { Checkout } from '@nextblock-cms/ecommerce/components/Checkout';
5
- import type { VisualEditAttributes } from '../../../lib/visual-editing/types';
6
-
7
- interface CheckoutBlockRendererProps {
8
- visualEditAttributes?: VisualEditAttributes;
9
- }
10
-
11
- export default function CheckoutBlockRenderer({
12
- visualEditAttributes,
13
- }: CheckoutBlockRendererProps) {
14
- return (
15
- <div {...visualEditAttributes}>
16
- <Checkout />
17
- </div>
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
+ }
@@ -1,22 +1,25 @@
1
- import React from 'react';
2
- import { FeaturedProductBlock } from '../../../lib/blocks/FeaturedProductBlock';
3
- import type { FeaturedProductBlockContent } from '../../../lib/blocks/ecommerce-block-schemas';
4
- import type { VisualEditAttributes } from '../../../lib/visual-editing/types';
5
-
6
- interface FeaturedProductBlockRendererProps {
7
- content: FeaturedProductBlockContent;
8
- languageId: number;
9
- visualEditAttributes?: VisualEditAttributes;
10
- }
11
-
12
- export default function FeaturedProductBlockRenderer({
13
- content,
14
- visualEditAttributes,
15
- }: FeaturedProductBlockRendererProps) {
16
-
17
- return (
18
- <div {...visualEditAttributes}>
19
- <FeaturedProductBlock content={content} />
20
- </div>
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
+ }