feat: product page layout toggle (flat/card), fix email shortcode rendering

- Add layout_style setting (flat default) to product appearance
  - AppearanceController: sanitize & persist layout_style, add to default settings
  - Admin SPA: Layout Style select in Appearance > Product
  - Customer SPA: useEffect targets <main> bg-white in flat mode (full-width),
    card mode uses per-section white floating cards on gray background
  - Accordion sections styled per mode: flat=border-t dividers, card=white cards

- Fix email shortcode gaps (EmailRenderer, EmailManager)
  - Add missing variables: return_url, contact_url, account_url (alias),
    payment_error_reason, order_items_list (alias for order_items_table)
  - Fix customer_note extra_data key mismatch (note → customer_note)
  - Pass low_stock_threshold via extra_data in low_stock email send
This commit is contained in:
Dwindi Ramadhana
2026-03-04 01:14:56 +07:00
parent 7ff429502d
commit 90169b508d
46 changed files with 2337 additions and 1278 deletions

View File

@@ -1,5 +1,6 @@
import { useState } from 'react';
import { cn } from '@/lib/utils';
import { getSectionBackground } from '@/lib/sectionStyles';
interface ContactFormSectionProps {
id: string;
@@ -23,6 +24,15 @@ export function ContactFormSection({
elementStyles,
styles,
}: ContactFormSectionProps & { styles?: Record<string, any> }) {
const heightMap: Record<string, string> = {
'default': 'py-12 md:py-20',
'small': 'py-8 md:py-12',
'medium': 'py-16 md:py-24',
'large': 'py-24 md:py-36',
'fullscreen': 'min-h-screen flex flex-col justify-center',
};
const customPadding = styles?.paddingTop || styles?.paddingBottom;
const heightClasses = customPadding ? '' : (heightMap[styles?.heightPreset || 'default'] || 'py-12 md:py-20');
const [formData, setFormData] = useState<Record<string, string>>({});
// Helper to get text styles (including font family)
@@ -87,6 +97,19 @@ export function ContactFormSection({
} finally {
setSubmitting(false);
}
}; const hasCustomBackground = !!styles?.backgroundColor || !!styles?.backgroundImage || styles?.backgroundType === 'gradient';
const sectionBg = getSectionBackground(styles);
// Helper to get background style for dynamic schemes
const getBackgroundStyle = (): React.CSSProperties | undefined => {
if (hasCustomBackground) return sectionBg.style;
if (colorScheme === 'primary') {
return { backgroundColor: 'var(--wn-primary, #1a1a1a)' };
}
if (colorScheme === 'secondary') {
return { backgroundColor: 'var(--wn-secondary, #6b7280)' };
}
return undefined;
};
return (
@@ -95,17 +118,18 @@ export function ContactFormSection({
className={cn(
'wn-section wn-contact-form',
`wn-scheme--${colorScheme}`,
`wn-scheme--${colorScheme}`,
'py-12 md:py-20',
heightClasses,
{
// 'bg-white': colorScheme === 'default', // Removed for global styling
'bg-muted': colorScheme === 'muted',
'bg-muted': colorScheme === 'muted' && !hasCustomBackground,
}
)}
style={getBackgroundStyle()}
>
<div className={cn(
"mx-auto px-4",
styles?.contentWidth === 'full' ? 'w-full' : 'container'
styles?.contentWidth === 'full' ? 'w-full'
: styles?.contentWidth === 'boxed' ? 'max-w-5xl'
: 'container'
)}>
<div className={cn(
'max-w-xl mx-auto',
@@ -116,7 +140,7 @@ export function ContactFormSection({
{title && (
<h2 className={cn(
"wn-contact__title text-center mb-12",
!elementStyles?.title?.fontSize && "text-3xl md:text-4xl",
!elementStyles?.title?.fontSize && "text-3xl md:text-4xl lg:text-5xl",
!elementStyles?.title?.fontWeight && "font-bold",
titleStyle.classNames
)}