Fix button roundtrip in editor, alignment persistence, and test email rendering

This commit is contained in:
Dwindi Ramadhana
2026-01-17 13:10:50 +07:00
parent 0e9ace902d
commit 6d2136d3b5
61 changed files with 8287 additions and 866 deletions

View File

@@ -36,13 +36,15 @@ export default function AppearanceGeneral() {
friendly: { name: 'Friendly', fonts: 'Poppins + Open Sans' },
elegant: { name: 'Elegant', fonts: 'Cormorant + Lato' },
};
const [colors, setColors] = useState({
primary: '#1a1a1a',
secondary: '#6b7280',
accent: '#3b82f6',
text: '#111827',
background: '#ffffff',
gradientStart: '#9333ea', // purple-600 defaults
gradientEnd: '#3b82f6', // blue-500 defaults
});
useEffect(() => {
@@ -51,7 +53,7 @@ export default function AppearanceGeneral() {
// Load appearance settings
const response = await api.get('/appearance/settings');
const general = response.data?.general;
if (general) {
if (general.spa_mode) setSpaMode(general.spa_mode);
if (general.spa_page) setSpaPage(general.spa_page || 0);
@@ -70,10 +72,12 @@ export default function AppearanceGeneral() {
accent: general.colors.accent || '#3b82f6',
text: general.colors.text || '#111827',
background: general.colors.background || '#ffffff',
gradientStart: general.colors.gradientStart || '#9333ea',
gradientEnd: general.colors.gradientEnd || '#3b82f6',
});
}
}
// Load available pages
const pagesResponse = await api.get('/pages/list');
console.log('Pages API response:', pagesResponse);
@@ -90,7 +94,7 @@ export default function AppearanceGeneral() {
setLoading(false);
}
};
loadSettings();
}, []);
@@ -108,7 +112,7 @@ export default function AppearanceGeneral() {
},
colors,
});
toast.success('General settings saved successfully');
} catch (error) {
console.error('Save error:', error);
@@ -139,7 +143,7 @@ export default function AppearanceGeneral() {
</p>
</div>
</div>
<div className="flex items-start space-x-3">
<RadioGroupItem value="checkout_only" id="spa-checkout" />
<div className="space-y-1">
@@ -151,7 +155,7 @@ export default function AppearanceGeneral() {
</p>
</div>
</div>
<div className="flex items-start space-x-3">
<RadioGroupItem value="full" id="spa-full" />
<div className="space-y-1">
@@ -175,14 +179,14 @@ export default function AppearanceGeneral() {
<Alert>
<AlertCircle className="h-4 w-4" />
<AlertDescription>
This page will render the full SPA to the body element with no theme interference.
This page will render the full SPA to the body element with no theme interference.
The SPA Mode above determines the initial route (shop or cart). React Router handles navigation via /#/ routing.
</AlertDescription>
</Alert>
<SettingsSection label="SPA Entry Page" htmlFor="spa-page">
<Select
value={spaPage.toString()}
<Select
value={spaPage.toString()}
onValueChange={(value) => setSpaPage(parseInt(value))}
>
<SelectTrigger id="spa-page">
@@ -246,7 +250,7 @@ export default function AppearanceGeneral() {
<p className="text-sm text-muted-foreground mb-3">
Self-hosted fonts, no external requests
</p>
{typographyMode === 'predefined' && (
<Select value={predefinedPair} onValueChange={setPredefinedPair}>
<SelectTrigger className="w-full min-w-[300px] [&>span]:line-clamp-none [&>span]:whitespace-normal">
@@ -284,7 +288,7 @@ export default function AppearanceGeneral() {
)}
</div>
</div>
<div className="flex items-start space-x-3">
<RadioGroupItem value="custom_google" id="typo-custom" />
<div className="space-y-1 flex-1">
@@ -297,7 +301,7 @@ export default function AppearanceGeneral() {
Using Google Fonts may not be GDPR compliant
</AlertDescription>
</Alert>
{typographyMode === 'custom_google' && (
<div className="space-y-3 mt-3">
<SettingsSection label="Heading Font" htmlFor="heading-font">
@@ -321,7 +325,7 @@ export default function AppearanceGeneral() {
</div>
</div>
</RadioGroup>
<div className="space-y-3 pt-4 border-t">
<Label>Font Scale: {fontScale[0].toFixed(1)}x</Label>
<Slider
@@ -345,18 +349,18 @@ export default function AppearanceGeneral() {
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{Object.entries(colors).map(([key, value]) => (
<SettingsSection key={key} label={key.charAt(0).toUpperCase() + key.slice(1)} htmlFor={`color-${key}`}>
<SettingsSection key={key} label={key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1')} htmlFor={`color-${key}`}>
<div className="flex gap-2">
<Input
id={`color-${key}`}
type="color"
value={value}
value={value as string}
onChange={(e) => setColors({ ...colors, [key]: e.target.value })}
className="w-20 h-10 cursor-pointer"
/>
<Input
type="text"
value={value}
value={value as string}
onChange={(e) => setColors({ ...colors, [key]: e.target.value })}
className="flex-1 font-mono"
/>