364 lines
14 KiB
TypeScript
364 lines
14 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { SettingsLayout } from '@/routes/Settings/components/SettingsLayout';
|
|
import { SettingsCard } from '@/routes/Settings/components/SettingsCard';
|
|
import { SettingsSection } from '@/routes/Settings/components/SettingsSection';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
|
import { toast } from 'sonner';
|
|
import { api } from '@/lib/api';
|
|
|
|
export default function AppearanceShop() {
|
|
const [loading, setLoading] = useState(true);
|
|
const [gridColumns, setGridColumns] = useState({
|
|
mobile: '2',
|
|
tablet: '3',
|
|
desktop: '4'
|
|
});
|
|
const [gridStyle, setGridStyle] = useState('standard');
|
|
const [cardStyle, setCardStyle] = useState('card');
|
|
const [aspectRatio, setAspectRatio] = useState('square');
|
|
const [filterLayout, setFilterLayout] = useState('basic');
|
|
|
|
const [elements, setElements] = useState({
|
|
category_filter: true,
|
|
search_bar: true,
|
|
sort_dropdown: true,
|
|
sale_badges: true,
|
|
});
|
|
|
|
const [saleBadgeColor, setSaleBadgeColor] = useState('#ef4444');
|
|
const [cardTextAlign, setCardTextAlign] = useState('left');
|
|
|
|
const [addToCartPosition, setAddToCartPosition] = useState('below');
|
|
const [addToCartStyle, setAddToCartStyle] = useState('solid');
|
|
const [showCartIcon, setShowCartIcon] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const loadSettings = async () => {
|
|
try {
|
|
const response = await api.get('/appearance/settings');
|
|
const shop = response.data?.pages?.shop;
|
|
|
|
if (shop) {
|
|
setGridColumns(shop.layout?.grid_columns || {
|
|
mobile: '2',
|
|
tablet: '3',
|
|
desktop: '4'
|
|
});
|
|
setGridStyle(shop.layout?.grid_style || 'standard');
|
|
setCardStyle(shop.layout?.card_style || 'card');
|
|
setAspectRatio(shop.layout?.aspect_ratio || 'square');
|
|
setCardTextAlign(shop.layout?.card_text_align || 'left');
|
|
setFilterLayout(shop.layout?.filter_layout || 'basic');
|
|
|
|
if (shop.elements) {
|
|
setElements(shop.elements);
|
|
}
|
|
|
|
setSaleBadgeColor(shop.sale_badge?.color || '#ef4444');
|
|
|
|
setAddToCartPosition(shop.add_to_cart?.position || 'below');
|
|
setAddToCartStyle(shop.add_to_cart?.style || 'solid');
|
|
setShowCartIcon(shop.add_to_cart?.show_icon ?? true);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to load settings:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
loadSettings();
|
|
}, []);
|
|
|
|
const toggleElement = (key: keyof typeof elements) => {
|
|
setElements({ ...elements, [key]: !elements[key] });
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
try {
|
|
await api.post('/appearance/pages/shop', {
|
|
layout: {
|
|
grid_columns: gridColumns,
|
|
grid_style: gridStyle,
|
|
card_style: cardStyle,
|
|
aspect_ratio: aspectRatio,
|
|
card_text_align: cardTextAlign,
|
|
filter_layout: filterLayout
|
|
},
|
|
elements: {
|
|
category_filter: elements.category_filter,
|
|
search_bar: elements.search_bar,
|
|
sort_dropdown: elements.sort_dropdown,
|
|
sale_badges: elements.sale_badges,
|
|
},
|
|
sale_badge: {
|
|
color: saleBadgeColor
|
|
},
|
|
add_to_cart: {
|
|
position: addToCartPosition,
|
|
style: addToCartStyle,
|
|
show_icon: showCartIcon
|
|
},
|
|
});
|
|
toast.success('Shop page settings saved successfully');
|
|
} catch (error) {
|
|
console.error('Save error:', error);
|
|
toast.error('Failed to save settings');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<SettingsLayout
|
|
title="Shop Page Settings"
|
|
onSave={handleSave}
|
|
isLoading={loading}
|
|
>
|
|
{/* Layout */}
|
|
<SettingsCard
|
|
title="Layout"
|
|
description="Configure shop page layout and product display"
|
|
>
|
|
<SettingsSection label="Grid Columns" description="Set columns for each breakpoint">
|
|
<div className="grid grid-cols-3 gap-4">
|
|
<div>
|
|
<Label htmlFor="grid-columns-mobile" className="text-sm font-medium mb-2 block">Mobile</Label>
|
|
<Select value={gridColumns.mobile} onValueChange={(value) => setGridColumns({ ...gridColumns, mobile: value })}>
|
|
<SelectTrigger id="grid-columns-mobile">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="1">1</SelectItem>
|
|
<SelectItem value="2">2</SelectItem>
|
|
<SelectItem value="3">3</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<p className="text-xs text-gray-500 mt-1"><768px</p>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="grid-columns-tablet" className="text-sm font-medium mb-2 block">Tablet</Label>
|
|
<Select value={gridColumns.tablet} onValueChange={(value) => setGridColumns({ ...gridColumns, tablet: value })}>
|
|
<SelectTrigger id="grid-columns-tablet">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="2">2</SelectItem>
|
|
<SelectItem value="3">3</SelectItem>
|
|
<SelectItem value="4">4</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<p className="text-xs text-gray-500 mt-1">768-1024px</p>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="grid-columns-desktop" className="text-sm font-medium mb-2 block">Desktop</Label>
|
|
<Select value={gridColumns.desktop} onValueChange={(value) => setGridColumns({ ...gridColumns, desktop: value })}>
|
|
<SelectTrigger id="grid-columns-desktop">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="2">2</SelectItem>
|
|
<SelectItem value="3">3</SelectItem>
|
|
<SelectItem value="4">4</SelectItem>
|
|
<SelectItem value="5">5</SelectItem>
|
|
<SelectItem value="6">6</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<p className="text-xs text-gray-500 mt-1">>1024px</p>
|
|
</div>
|
|
</div>
|
|
</SettingsSection>
|
|
|
|
<SettingsSection label="Grid Style" htmlFor="grid-style" description="Masonry creates a Pinterest-like layout with varying heights">
|
|
<Select value={gridStyle} onValueChange={setGridStyle}>
|
|
<SelectTrigger id="grid-style">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="standard">Standard - Equal heights</SelectItem>
|
|
<SelectItem value="masonry">Masonry - Dynamic heights</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</SettingsSection>
|
|
|
|
<SettingsSection label="Filter Layout" htmlFor="filter-layout" description="Choose how catalog filters are presented">
|
|
<Select value={filterLayout} onValueChange={setFilterLayout}>
|
|
<SelectTrigger id="filter-layout">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="basic">Basic - Horizontal Top Bar</SelectItem>
|
|
<SelectItem value="rich_sidebar">Rich - Left Sidebar</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</SettingsSection>
|
|
|
|
<SettingsSection label="Product Card Style" htmlFor="card-style" description="Visual style adapts to column count - more columns = cleaner style">
|
|
<Select value={cardStyle} onValueChange={setCardStyle}>
|
|
<SelectTrigger id="card-style">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="card">Card - Bordered with shadow</SelectItem>
|
|
<SelectItem value="minimal">Minimal - Clean, no border</SelectItem>
|
|
<SelectItem value="overlay">Overlay - Shadow on hover</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</SettingsSection>
|
|
|
|
<SettingsSection label="Image Aspect Ratio" htmlFor="aspect-ratio">
|
|
<Select value={aspectRatio} onValueChange={setAspectRatio}>
|
|
<SelectTrigger id="aspect-ratio">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="square">Square (1:1)</SelectItem>
|
|
<SelectItem value="portrait">Portrait (3:4)</SelectItem>
|
|
<SelectItem value="landscape">Landscape (4:3)</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</SettingsSection>
|
|
|
|
<SettingsSection label="Card Text Alignment" htmlFor="card-text-align" description="Align product title and price">
|
|
<Select value={cardTextAlign} onValueChange={setCardTextAlign}>
|
|
<SelectTrigger id="card-text-align">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="left">Left</SelectItem>
|
|
<SelectItem value="center">Center</SelectItem>
|
|
<SelectItem value="right">Right</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</SettingsSection>
|
|
|
|
<SettingsSection label="Sale Badge Color" htmlFor="sale-badge-color">
|
|
<input
|
|
type="color"
|
|
id="sale-badge-color"
|
|
value={saleBadgeColor}
|
|
onChange={(e) => setSaleBadgeColor(e.target.value)}
|
|
className="h-10 w-full rounded-md border border-input cursor-pointer"
|
|
/>
|
|
</SettingsSection>
|
|
</SettingsCard>
|
|
|
|
{/* Elements */}
|
|
<SettingsCard
|
|
title="Elements"
|
|
description="Choose which elements to display on the shop page"
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<Label htmlFor="element-category_filter" className="cursor-pointer">
|
|
Show category filter
|
|
</Label>
|
|
<Switch
|
|
id="element-category_filter"
|
|
checked={elements.category_filter}
|
|
onCheckedChange={() => toggleElement('category_filter')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<Label htmlFor="element-search_bar" className="cursor-pointer">
|
|
Show search bar
|
|
</Label>
|
|
<Switch
|
|
id="element-search_bar"
|
|
checked={elements.search_bar}
|
|
onCheckedChange={() => toggleElement('search_bar')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<Label htmlFor="element-sort_dropdown" className="cursor-pointer">
|
|
Show sort dropdown
|
|
</Label>
|
|
<Switch
|
|
id="element-sort_dropdown"
|
|
checked={elements.sort_dropdown}
|
|
onCheckedChange={() => toggleElement('sort_dropdown')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<Label htmlFor="element-sale_badges" className="cursor-pointer">
|
|
Show sale badges
|
|
</Label>
|
|
<Switch
|
|
id="element-sale_badges"
|
|
checked={elements.sale_badges}
|
|
onCheckedChange={() => toggleElement('sale_badges')}
|
|
/>
|
|
</div>
|
|
</SettingsCard>
|
|
|
|
{/* Add to Cart Button */}
|
|
<SettingsCard
|
|
title="Add to Cart Button"
|
|
description="Configure add to cart button appearance and behavior"
|
|
>
|
|
<SettingsSection label="Position">
|
|
<RadioGroup value={addToCartPosition} onValueChange={setAddToCartPosition}>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="below" id="position-below" />
|
|
<Label htmlFor="position-below" className="cursor-pointer">
|
|
Below image
|
|
</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="overlay" id="position-overlay" />
|
|
<Label htmlFor="position-overlay" className="cursor-pointer">
|
|
On hover overlay
|
|
</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="bottom" id="position-bottom" />
|
|
<Label htmlFor="position-bottom" className="cursor-pointer">
|
|
Bottom of card
|
|
</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</SettingsSection>
|
|
|
|
<SettingsSection label="Style">
|
|
<RadioGroup value={addToCartStyle} onValueChange={setAddToCartStyle}>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="solid" id="style-solid" />
|
|
<Label htmlFor="style-solid" className="cursor-pointer">
|
|
Solid
|
|
</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="outline" id="style-outline" />
|
|
<Label htmlFor="style-outline" className="cursor-pointer">
|
|
Outline
|
|
</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="text" id="style-text" />
|
|
<Label htmlFor="style-text" className="cursor-pointer">
|
|
Text only
|
|
</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</SettingsSection>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<Label htmlFor="show-cart-icon" className="cursor-pointer">
|
|
Show cart icon
|
|
</Label>
|
|
<Switch
|
|
id="show-cart-icon"
|
|
checked={showCartIcon}
|
|
onCheckedChange={setShowCartIcon}
|
|
/>
|
|
</div>
|
|
</SettingsCard>
|
|
</SettingsLayout>
|
|
);
|
|
}
|