@zevcommerce/theme-starter 1.0.0
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 +27 -0
- package/src/components/ThemeStyles.tsx +92 -0
- package/src/helpers/format-price.ts +50 -0
- package/src/index.ts +26 -0
- package/src/preset.json +126 -0
- package/src/registry.ts +35 -0
- package/src/sections/Announcement.tsx +30 -0
- package/src/sections/CartSection.tsx +156 -0
- package/src/sections/CheckoutSection.tsx +21 -0
- package/src/sections/ContactInfo.tsx +129 -0
- package/src/sections/FeaturedProducts.tsx +114 -0
- package/src/sections/Footer.tsx +167 -0
- package/src/sections/Header.tsx +307 -0
- package/src/sections/Hero.tsx +83 -0
- package/src/sections/ProductDetail.tsx +252 -0
- package/src/sections/ProductList.tsx +76 -0
- package/src/settings.ts +140 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +9 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useTheme } from '@zevcommerce/storefront-api';
|
|
4
|
+
|
|
5
|
+
export default function ContactInfo() {
|
|
6
|
+
const { theme } = useTheme();
|
|
7
|
+
const contact = theme?.settings?.contact;
|
|
8
|
+
|
|
9
|
+
if (!contact?.enabled) return null;
|
|
10
|
+
|
|
11
|
+
const heading = contact.heading || 'Get in Touch';
|
|
12
|
+
const showPhone = contact.showPhone && contact.phone;
|
|
13
|
+
const showEmail = contact.showEmail && contact.email;
|
|
14
|
+
const showWhatsApp = contact.showWhatsApp && contact.whatsapp;
|
|
15
|
+
const showAddress = contact.showAddress && contact.address;
|
|
16
|
+
|
|
17
|
+
const hasItems = showPhone || showEmail || showWhatsApp || showAddress;
|
|
18
|
+
if (!hasItems) return null;
|
|
19
|
+
|
|
20
|
+
const cards: Array<{ icon: React.ReactNode; label: string; value: string; href?: string }> = [];
|
|
21
|
+
|
|
22
|
+
if (showPhone) {
|
|
23
|
+
cards.push({
|
|
24
|
+
icon: (
|
|
25
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
26
|
+
<path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 16.92z" />
|
|
27
|
+
</svg>
|
|
28
|
+
),
|
|
29
|
+
label: 'Phone',
|
|
30
|
+
value: contact.phone,
|
|
31
|
+
href: `tel:${contact.phone}`,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (showEmail) {
|
|
36
|
+
cards.push({
|
|
37
|
+
icon: (
|
|
38
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
39
|
+
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
|
|
40
|
+
<polyline points="22,6 12,13 2,6" />
|
|
41
|
+
</svg>
|
|
42
|
+
),
|
|
43
|
+
label: 'Email',
|
|
44
|
+
value: contact.email,
|
|
45
|
+
href: `mailto:${contact.email}`,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (showWhatsApp) {
|
|
50
|
+
const waNumber = contact.whatsapp.replace(/[^0-9]/g, '');
|
|
51
|
+
cards.push({
|
|
52
|
+
icon: (
|
|
53
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
54
|
+
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" />
|
|
55
|
+
</svg>
|
|
56
|
+
),
|
|
57
|
+
label: 'WhatsApp',
|
|
58
|
+
value: contact.whatsapp,
|
|
59
|
+
href: `https://wa.me/${waNumber}`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (showAddress) {
|
|
64
|
+
cards.push({
|
|
65
|
+
icon: (
|
|
66
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
67
|
+
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z" />
|
|
68
|
+
<circle cx="12" cy="10" r="3" />
|
|
69
|
+
</svg>
|
|
70
|
+
),
|
|
71
|
+
label: 'Address',
|
|
72
|
+
value: contact.address,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<section className="py-12 md:py-16" style={{ backgroundColor: 'var(--color-background)' }}>
|
|
78
|
+
<div className="container mx-auto px-4 sm:px-6">
|
|
79
|
+
<h2
|
|
80
|
+
className="text-2xl md:text-3xl font-bold text-center mb-10"
|
|
81
|
+
style={{ color: 'var(--color-text)' }}
|
|
82
|
+
>
|
|
83
|
+
{heading}
|
|
84
|
+
</h2>
|
|
85
|
+
|
|
86
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 max-w-4xl mx-auto">
|
|
87
|
+
{cards.map((card, i) => {
|
|
88
|
+
const content = (
|
|
89
|
+
<div
|
|
90
|
+
key={i}
|
|
91
|
+
className="flex flex-col items-center text-center p-6 rounded-lg border transition-shadow hover:shadow-md"
|
|
92
|
+
style={{ borderColor: '#e5e7eb' }}
|
|
93
|
+
>
|
|
94
|
+
<div
|
|
95
|
+
className="mb-3"
|
|
96
|
+
style={{ color: 'var(--color-primary)' }}
|
|
97
|
+
>
|
|
98
|
+
{card.icon}
|
|
99
|
+
</div>
|
|
100
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider mb-1" style={{ color: 'var(--color-text)', opacity: 0.5 }}>
|
|
101
|
+
{card.label}
|
|
102
|
+
</h3>
|
|
103
|
+
<p className="text-sm font-medium" style={{ color: 'var(--color-text)' }}>
|
|
104
|
+
{card.value}
|
|
105
|
+
</p>
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
if (card.href) {
|
|
110
|
+
return (
|
|
111
|
+
<a key={i} href={card.href} target={card.label === 'WhatsApp' ? '_blank' : undefined} rel="noopener noreferrer">
|
|
112
|
+
{content}
|
|
113
|
+
</a>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return content;
|
|
117
|
+
})}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</section>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const schema = {
|
|
125
|
+
type: 'contact-info',
|
|
126
|
+
name: 'Contact Info',
|
|
127
|
+
limit: 1,
|
|
128
|
+
settings: [],
|
|
129
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import { useTheme, getCollection, ProductCard, getStorePermalink } from '@zevcommerce/storefront-api';
|
|
6
|
+
import { useParams } from 'next/navigation';
|
|
7
|
+
|
|
8
|
+
export default function FeaturedProducts() {
|
|
9
|
+
const { theme, storeConfig } = useTheme();
|
|
10
|
+
const params = useParams();
|
|
11
|
+
const domain = (params?.domain as string) || storeConfig?.handle || '';
|
|
12
|
+
|
|
13
|
+
const products_settings = theme?.settings?.products;
|
|
14
|
+
if (!products_settings?.enabled) return null;
|
|
15
|
+
|
|
16
|
+
const heading = products_settings.heading || 'Featured Products';
|
|
17
|
+
const collectionHandle = products_settings.collection || 'all';
|
|
18
|
+
const limit = parseInt(products_settings.limit || '8');
|
|
19
|
+
const columns = parseInt(products_settings.columns || '4');
|
|
20
|
+
|
|
21
|
+
const [products, setProducts] = useState<any[]>([]);
|
|
22
|
+
const [loading, setLoading] = useState(true);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
async function fetchData() {
|
|
26
|
+
if (!domain || !collectionHandle) {
|
|
27
|
+
setLoading(false);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
setLoading(true);
|
|
31
|
+
try {
|
|
32
|
+
const collection = await getCollection(domain, collectionHandle);
|
|
33
|
+
if (collection) {
|
|
34
|
+
const productList = collection.products?.map((p: any) => p.product || p) || [];
|
|
35
|
+
setProducts(productList.slice(0, limit));
|
|
36
|
+
}
|
|
37
|
+
} catch (error: any) {
|
|
38
|
+
if (error?.response?.status !== 404) {
|
|
39
|
+
console.error('Error fetching products:', error);
|
|
40
|
+
}
|
|
41
|
+
} finally {
|
|
42
|
+
setLoading(false);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
fetchData();
|
|
46
|
+
}, [domain, collectionHandle, limit]);
|
|
47
|
+
|
|
48
|
+
const gridColsMap: Record<number, string> = {
|
|
49
|
+
2: 'sm:grid-cols-2',
|
|
50
|
+
3: 'sm:grid-cols-2 lg:grid-cols-3',
|
|
51
|
+
4: 'sm:grid-cols-2 lg:grid-cols-4',
|
|
52
|
+
};
|
|
53
|
+
const gridColsClass = gridColsMap[columns] || 'sm:grid-cols-2 lg:grid-cols-4';
|
|
54
|
+
|
|
55
|
+
if (loading) {
|
|
56
|
+
return (
|
|
57
|
+
<section className="py-12 md:py-16" style={{ backgroundColor: 'var(--color-background)' }}>
|
|
58
|
+
<div className="container mx-auto px-4 sm:px-6">
|
|
59
|
+
<div className="animate-pulse">
|
|
60
|
+
<div className="h-8 w-48 mb-8 rounded" style={{ backgroundColor: '#e5e7eb' }} />
|
|
61
|
+
<div className={`grid grid-cols-2 ${gridColsClass} gap-4 sm:gap-6`}>
|
|
62
|
+
{[...Array(columns)].map((_, i) => (
|
|
63
|
+
<div key={i} className="aspect-[3/4] rounded-lg" style={{ backgroundColor: '#e5e7eb' }} />
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</section>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (products.length === 0) return null;
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<section className="py-12 md:py-16" style={{ backgroundColor: 'var(--color-background)' }}>
|
|
76
|
+
<div className="container mx-auto px-4 sm:px-6">
|
|
77
|
+
{/* Header */}
|
|
78
|
+
<div className="flex items-center justify-between mb-8">
|
|
79
|
+
<h2
|
|
80
|
+
className="text-2xl md:text-3xl font-bold"
|
|
81
|
+
style={{ color: 'var(--color-text)' }}
|
|
82
|
+
>
|
|
83
|
+
{heading}
|
|
84
|
+
</h2>
|
|
85
|
+
<Link
|
|
86
|
+
href={getStorePermalink(domain, `/collections/${collectionHandle}`)}
|
|
87
|
+
className="text-sm font-medium transition-opacity hover:opacity-70"
|
|
88
|
+
style={{ color: 'var(--color-primary)' }}
|
|
89
|
+
>
|
|
90
|
+
View All
|
|
91
|
+
</Link>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
{/* Grid */}
|
|
95
|
+
<div className={`grid grid-cols-2 ${gridColsClass} gap-4 sm:gap-6`}>
|
|
96
|
+
{products.map(product => (
|
|
97
|
+
<ProductCard
|
|
98
|
+
key={product.id}
|
|
99
|
+
product={product}
|
|
100
|
+
domain={domain}
|
|
101
|
+
/>
|
|
102
|
+
))}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</section>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const schema = {
|
|
110
|
+
type: 'featured-products',
|
|
111
|
+
name: 'Featured Products',
|
|
112
|
+
limit: 1,
|
|
113
|
+
settings: [],
|
|
114
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import { useTheme, resolveMenuUrl, getStorePermalink } from '@zevcommerce/storefront-api';
|
|
5
|
+
import { useParams } from 'next/navigation';
|
|
6
|
+
|
|
7
|
+
export default function Footer() {
|
|
8
|
+
const { theme, storeConfig, menus } = useTheme();
|
|
9
|
+
const params = useParams();
|
|
10
|
+
const domain = (params?.domain as string) || storeConfig?.handle || '';
|
|
11
|
+
|
|
12
|
+
const footer = theme?.settings?.footer;
|
|
13
|
+
const storeName = storeConfig?.name || 'Store';
|
|
14
|
+
const logoSrc = storeConfig?.storeLogo;
|
|
15
|
+
|
|
16
|
+
const description = footer?.description || '';
|
|
17
|
+
const menuHandle = footer?.menuHandle || 'footer';
|
|
18
|
+
const copyright = footer?.copyright || `${new Date().getFullYear()} ${storeName}. All rights reserved.`;
|
|
19
|
+
|
|
20
|
+
// Social links
|
|
21
|
+
const socialLinks: Array<{ platform: string; url: string; icon: React.ReactNode }> = [];
|
|
22
|
+
|
|
23
|
+
if (footer?.instagram) {
|
|
24
|
+
socialLinks.push({
|
|
25
|
+
platform: 'Instagram',
|
|
26
|
+
url: footer.instagram,
|
|
27
|
+
icon: (
|
|
28
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
29
|
+
<rect x="2" y="2" width="20" height="20" rx="5" ry="5" />
|
|
30
|
+
<circle cx="12" cy="12" r="4" />
|
|
31
|
+
<circle cx="17.5" cy="6.5" r="0.5" fill="currentColor" />
|
|
32
|
+
</svg>
|
|
33
|
+
),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (footer?.facebook) {
|
|
38
|
+
socialLinks.push({
|
|
39
|
+
platform: 'Facebook',
|
|
40
|
+
url: footer.facebook,
|
|
41
|
+
icon: (
|
|
42
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
|
43
|
+
<path d="M18 2h-3a5 5 0 00-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 011-1h3z" />
|
|
44
|
+
</svg>
|
|
45
|
+
),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (footer?.twitter) {
|
|
50
|
+
socialLinks.push({
|
|
51
|
+
platform: 'Twitter',
|
|
52
|
+
url: footer.twitter,
|
|
53
|
+
icon: (
|
|
54
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
|
55
|
+
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
|
56
|
+
</svg>
|
|
57
|
+
),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (footer?.tiktok) {
|
|
62
|
+
socialLinks.push({
|
|
63
|
+
platform: 'TikTok',
|
|
64
|
+
url: footer.tiktok,
|
|
65
|
+
icon: (
|
|
66
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
|
67
|
+
<path d="M19.59 6.69a4.83 4.83 0 01-3.77-4.25V2h-3.45v13.67a2.89 2.89 0 01-2.88 2.5 2.89 2.89 0 01-2.89-2.89 2.89 2.89 0 012.89-2.89c.28 0 .54.04.79.1V9.01a6.33 6.33 0 00-.79-.05 6.34 6.34 0 00-6.34 6.34 6.34 6.34 0 006.34 6.34 6.34 6.34 0 006.33-6.34V9.04a8.28 8.28 0 004.84 1.55V7.14a4.85 4.85 0 01-1.07-.45z" />
|
|
68
|
+
</svg>
|
|
69
|
+
),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Footer menu
|
|
74
|
+
const footerMenu = menus?.[menuHandle];
|
|
75
|
+
const menuItems = (footerMenu as any)?.items || [];
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<footer
|
|
79
|
+
className="py-12 md:py-16 border-t"
|
|
80
|
+
style={{ backgroundColor: 'var(--color-background)', borderColor: '#e5e7eb' }}
|
|
81
|
+
>
|
|
82
|
+
<div className="container mx-auto px-4 sm:px-6">
|
|
83
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-12">
|
|
84
|
+
{/* Brand column */}
|
|
85
|
+
<div>
|
|
86
|
+
<Link href={getStorePermalink(domain, '/')} className="inline-block mb-4">
|
|
87
|
+
{logoSrc ? (
|
|
88
|
+
<img src={logoSrc} alt={storeName} className="h-8 w-auto object-contain" />
|
|
89
|
+
) : (
|
|
90
|
+
<span className="text-lg font-bold" style={{ color: 'var(--color-text)' }}>{storeName}</span>
|
|
91
|
+
)}
|
|
92
|
+
</Link>
|
|
93
|
+
{description && (
|
|
94
|
+
<p className="text-sm leading-relaxed max-w-xs" style={{ color: 'var(--color-text)', opacity: 0.6 }}>
|
|
95
|
+
{description}
|
|
96
|
+
</p>
|
|
97
|
+
)}
|
|
98
|
+
{/* Social icons */}
|
|
99
|
+
{socialLinks.length > 0 && (
|
|
100
|
+
<div className="flex items-center gap-4 mt-5">
|
|
101
|
+
{socialLinks.map((link, i) => (
|
|
102
|
+
<a
|
|
103
|
+
key={i}
|
|
104
|
+
href={link.url}
|
|
105
|
+
target="_blank"
|
|
106
|
+
rel="noopener noreferrer"
|
|
107
|
+
aria-label={link.platform}
|
|
108
|
+
className="transition-opacity hover:opacity-70"
|
|
109
|
+
style={{ color: 'var(--color-text)', opacity: 0.5 }}
|
|
110
|
+
>
|
|
111
|
+
{link.icon}
|
|
112
|
+
</a>
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
{/* Footer menu */}
|
|
119
|
+
{menuItems.length > 0 && (
|
|
120
|
+
<div>
|
|
121
|
+
<h3 className="text-sm font-semibold uppercase tracking-wider mb-4" style={{ color: 'var(--color-text)' }}>
|
|
122
|
+
Quick Links
|
|
123
|
+
</h3>
|
|
124
|
+
<nav className="flex flex-col gap-2.5">
|
|
125
|
+
{menuItems.map((item: any) => (
|
|
126
|
+
<Link
|
|
127
|
+
key={item.id}
|
|
128
|
+
href={resolveMenuUrl(item, domain)}
|
|
129
|
+
className="text-sm transition-opacity hover:opacity-70"
|
|
130
|
+
style={{ color: 'var(--color-text)', opacity: 0.6 }}
|
|
131
|
+
>
|
|
132
|
+
{item.title}
|
|
133
|
+
</Link>
|
|
134
|
+
))}
|
|
135
|
+
</nav>
|
|
136
|
+
</div>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
{/* Store info */}
|
|
140
|
+
<div>
|
|
141
|
+
<h3 className="text-sm font-semibold uppercase tracking-wider mb-4" style={{ color: 'var(--color-text)' }}>
|
|
142
|
+
Store
|
|
143
|
+
</h3>
|
|
144
|
+
<div className="flex flex-col gap-2.5 text-sm" style={{ color: 'var(--color-text)', opacity: 0.6 }}>
|
|
145
|
+
{storeConfig?.email && <p>{storeConfig.email}</p>}
|
|
146
|
+
{storeConfig?.phone && <p>{storeConfig.phone}</p>}
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
{/* Copyright */}
|
|
152
|
+
<div className="mt-12 pt-8 border-t text-center" style={{ borderColor: '#e5e7eb' }}>
|
|
153
|
+
<p className="text-xs" style={{ color: 'var(--color-text)', opacity: 0.4 }}>
|
|
154
|
+
© {copyright}
|
|
155
|
+
</p>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</footer>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const schema = {
|
|
163
|
+
type: 'footer',
|
|
164
|
+
name: 'Footer',
|
|
165
|
+
limit: 1,
|
|
166
|
+
settings: [],
|
|
167
|
+
};
|