Yaltopia-Hotel-Emails/src/email/EmailShell.tsx
“kirukib” 1e383b5ba8 structured contact footer
Made-with: Cursor
2026-04-02 11:29:29 +03:00

246 lines
8.7 KiB
TypeScript

import type { ReactNode } from 'react'
import {
Html,
Head,
Body,
Container,
Section,
Text,
Heading,
Hr,
Img,
Preview,
Link,
Row,
Column,
} from '@react-email/components'
import type { Brand } from './types'
type EmailShellProps = {
brand: Brand
title: string
previewText: string
children: ReactNode
}
const DEFAULT_LOGO_PLACEHOLDER =
'https://dummyimage.com/120x40/0f172a/ffffff&text=Shitaye'
function IconMail() {
return (
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path
fill="currentColor"
d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4-8 5-8-5V6l8 5 8-5v2z"
/>
</svg>
)
}
function IconPhone() {
return (
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path
fill="currentColor"
d="M6.6 10.8c1.5 2.9 3.9 5.3 6.8 6.8l2.2-2.2c.3-.3.8-.4 1.2-.2 1.3.4 2.7.7 4.1.7.6 0 1 .4 1 1V21c0 .6-.4 1-1 1C10.1 22 2 13.9 2 4c0-.6.4-1 1-1h3.9c.6 0 1 .4 1 1 0 1.4.2 2.8.7 4.1.1.4 0 .9-.2 1.2L6.6 10.8z"
/>
</svg>
)
}
function ContactLine({
icon,
children,
}: {
icon: ReactNode
children: ReactNode
}) {
return (
<Row style={{ margin: '0 0 8px' }}>
<Column style={{ width: 24, paddingRight: 8 }}>
<Text style={{ margin: 0, color: 'inherit', display: 'block' }}>{icon}</Text>
</Column>
<Column style={{ width: 'auto' }}>{children}</Column>
</Row>
)
}
export function EmailShell({ brand, title, previewText, children }: EmailShellProps) {
const logoUrl = brand.logoUrl?.trim() ? brand.logoUrl.trim() : DEFAULT_LOGO_PLACEHOLDER
return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Body style={{ margin: '0', padding: '0', backgroundColor: brand.backgroundColor || '#f8fafc' }}>
<Container
style={{
maxWidth: 560,
width: '100%',
margin: '0 auto',
padding: '0 16px',
fontFamily:
'ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"',
}}
>
<Section style={{ padding: '24px 0 18px', borderBottom: `1px solid ${brand.secondaryColor}` }}>
<Img
src={logoUrl}
width={120}
height={40}
alt={brand.hotelName}
style={{ objectFit: 'contain', display: 'block', margin: '0 0 10px' }}
/>
<Heading style={{ fontSize: 20, margin: '0', color: brand.textColor }}>{brand.hotelName}</Heading>
<Text style={{ margin: '6px 0 0', color: brand.textColor, opacity: 0.9 }}>{title}</Text>
</Section>
<Section style={{ padding: '22px 0' }}>{children}</Section>
<Hr style={{ borderColor: brand.secondaryColor, opacity: 0.2 }} />
<Section style={{ padding: '18px 0 28px' }}>
<Section
style={{
border: `1px solid ${brand.secondaryColor}`,
borderRadius: 12,
padding: 14,
backgroundColor: 'rgba(255,255,255,0.55)',
}}
>
{brand.footer.address ? (
<Text style={{ margin: 0, color: brand.textColor, fontSize: 12, lineHeight: '18px' }}>
{brand.footer.address}
</Text>
) : null}
{brand.footer.email ? (
<ContactLine icon={<IconMail />}>
<Text style={{ margin: 0, color: brand.textColor, fontSize: 12, lineHeight: '18px' }}>
<Link
href={`mailto:${brand.footer.email}`}
style={{ color: brand.primaryColor, textDecoration: 'none' }}
>
{brand.footer.email}
</Link>
</Text>
</ContactLine>
) : null}
{brand.footer.phone1 || brand.footer.phone2 ? (
<ContactLine icon={<IconPhone />}>
<Text style={{ margin: 0, color: brand.textColor, fontSize: 12, lineHeight: '18px' }}>
{brand.footer.phone1 ? brand.footer.phone1 : ''}
{brand.footer.phone1 && brand.footer.phone2 ? ' · ' : ''}
{brand.footer.phone2 ? brand.footer.phone2 : ''}
</Text>
</ContactLine>
) : null}
</Section>
<Section style={{ margin: '14px 0 0', padding: 0 }}>
<Text style={{ margin: '0 0 8px', color: brand.textColor, fontSize: 12, opacity: 0.85, lineHeight: '18px' }}>
Follow us
</Text>
<Link
href={brand.footer.socialFacebookUrl || '#'}
style={{
display: 'inline-block',
marginRight: 8,
marginBottom: 8,
padding: '8px 12px',
borderRadius: 999,
backgroundColor: brand.primaryColor,
color: '#ffffff',
textDecoration: 'none',
fontSize: 12,
fontWeight: 700,
}}
>
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path
fill="#ffffff"
d="M14.5 3H17v3.2h-2.3c-.9 0-1.2.4-1.2 1.1V10H17l-.5 3.3h-3v7.7H10.3v-7.7H7.9V10h2.4V8c0-2.2 1.1-5 4.2-5z"
/>
</svg>
</Link>
<Link
href={brand.footer.socialInstagramUrl || '#'}
style={{
display: 'inline-block',
marginRight: 8,
marginBottom: 8,
padding: '8px 12px',
borderRadius: 999,
backgroundColor: brand.primaryColor,
color: '#ffffff',
textDecoration: 'none',
fontSize: 12,
fontWeight: 700,
}}
>
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path
fill="#ffffff"
d="M7 2h10a5 5 0 0 1 5 5v10a5 5 0 0 1-5 5H7a5 5 0 0 1-5-5V7a5 5 0 0 1 5-5zm5 5.7A3.3 3.3 0 1 0 15.3 11 3.3 3.3 0 0 0 12 7.7zm6.2-.9a1 1 0 1 0 0 .1zM12 9a2 2 0 1 1-2 2 2 2 0 0 1 2-2z"
/>
<path
fill="#ffffff"
d="M17.5 2.7h.1"
opacity="0"
/>
</svg>
</Link>
<Link
href={brand.footer.socialTwitterUrl || '#'}
style={{
display: 'inline-block',
marginRight: 8,
marginBottom: 8,
padding: '8px 12px',
borderRadius: 999,
backgroundColor: brand.primaryColor,
color: '#ffffff',
textDecoration: 'none',
fontSize: 12,
fontWeight: 700,
}}
>
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path
fill="#ffffff"
d="M18.3 2H21l-6.5 7.4L22 22h-6.2l-4.9-6.3L5 22H2.3l7.1-8.1L2 2h6.3l4.4 5.6L18.3 2zm-1.1 18h1.7L7.5 3.9H5.7L17.2 20z"
/>
</svg>
</Link>
<Link
href={brand.footer.socialLinkedInUrl || '#'}
style={{
display: 'inline-block',
padding: '8px 12px',
borderRadius: 999,
backgroundColor: brand.primaryColor,
color: '#ffffff',
textDecoration: 'none',
fontSize: 12,
fontWeight: 700,
}}
>
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path
fill="#ffffff"
d="M6.5 6.8a2.2 2.2 0 1 1 0-4.4 2.2 2.2 0 0 1 0 4.4zM4.5 21.5V8.5h4v13h-4zM10.5 8.5h3.8v1.8h.1c.5-1 1.8-2 3.7-2 4 0 4.7 2.6 4.7 6v7.2h-4V15c0-1.3 0-3-1.8-3s-2.1 1.4-2.1 2.9v6.6h-4v-13z"
/>
</svg>
</Link>
</Section>
<Text style={{ margin: '14px 0 0', color: brand.textColor, fontSize: 12, lineHeight: '18px' }}>
© {new Date().getFullYear()} {brand.hotelName}. All rights reserved.
</Text>
</Section>
</Container>
</Body>
</Html>
)
}