feat: obfuscate email with click-to-reveal across all pages
Replace all bare info@ occurrences with a click-to-reveal pattern: - New EmailLink React component (base64 decode on click, never in DOM pre-click) - privacy, terms, refund pages use EmailLink - contact/index.html uses a vanilla JS button with the same pattern - PaymentForm mailto builder uses atob() to keep email out of source literals Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3330c47af2
commit
57cc5840b9
@ -1,4 +1,5 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
|
import EmailLink from '@/components/EmailLink'
|
||||||
|
|
||||||
export const metadata: Metadata = { title: 'Privacy Policy' }
|
export const metadata: Metadata = { title: 'Privacy Policy' }
|
||||||
|
|
||||||
@ -75,13 +76,12 @@ export default function PrivacyPage() {
|
|||||||
<h2>8. Your rights</h2>
|
<h2>8. Your rights</h2>
|
||||||
<p>
|
<p>
|
||||||
You may request access to, correction of, or deletion of your personal data at any
|
You may request access to, correction of, or deletion of your personal data at any
|
||||||
time by emailing <a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a>.
|
time by emailing <EmailLink label="us directly" />.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>9. Contact</h2>
|
<h2>9. Contact</h2>
|
||||||
<p>
|
<p>
|
||||||
Questions about this policy? Email us at{' '}
|
Questions about this policy? <EmailLink label="Email us" /> or call{' '}
|
||||||
<a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a> or call{' '}
|
|
||||||
<a href="tel:+12035551234">(203) 555-1234</a>.
|
<a href="tel:+12035551234">(203) 555-1234</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
|
import EmailLink from '@/components/EmailLink'
|
||||||
|
|
||||||
export const metadata: Metadata = { title: 'Refund & Cancellation Policy' }
|
export const metadata: Metadata = { title: 'Refund & Cancellation Policy' }
|
||||||
|
|
||||||
@ -19,8 +20,7 @@ export default function RefundPage() {
|
|||||||
<strong>75% store credit</strong>.
|
<strong>75% store credit</strong>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
To cancel, please contact us as soon as possible at{' '}
|
To cancel, please contact us as soon as possible — <EmailLink label="email us" />.
|
||||||
<a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a>.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>After Delivery or Pickup</h2>
|
<h2>After Delivery or Pickup</h2>
|
||||||
@ -45,7 +45,7 @@ export default function RefundPage() {
|
|||||||
|
|
||||||
<h2>Questions?</h2>
|
<h2>Questions?</h2>
|
||||||
<p>
|
<p>
|
||||||
Email <a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a> —
|
<EmailLink label="Email us" /> —
|
||||||
we’re a small local business and we’ll always do our best to make things right.
|
we’re a small local business and we’ll always do our best to make things right.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
|
import EmailLink from '@/components/EmailLink'
|
||||||
|
|
||||||
export const metadata: Metadata = { title: 'Terms & Conditions' }
|
export const metadata: Metadata = { title: 'Terms & Conditions' }
|
||||||
|
|
||||||
@ -32,8 +33,7 @@ export default function TermsPage() {
|
|||||||
be resolved in the courts of New Haven County, Connecticut.
|
be resolved in the courts of New Haven County, Connecticut.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Questions or concerns? Email us at{' '}
|
Questions or concerns? <EmailLink label="Email us" />.
|
||||||
<a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a>.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|||||||
42
estore/src/components/EmailLink.tsx
Normal file
42
estore/src/components/EmailLink.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
const ENCODED = 'aW5mb0BiZWFjaHBhcnR5YmFsbG9vbnMuY29t'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label?: string
|
||||||
|
subject?: string
|
||||||
|
body?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EmailLink({ label = 'click to reveal', subject, body }: Props) {
|
||||||
|
const [addr, setAddr] = useState<string | null>(null)
|
||||||
|
|
||||||
|
if (!addr) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setAddr(atob(ENCODED))}
|
||||||
|
style={{
|
||||||
|
background: 'none',
|
||||||
|
border: 'none',
|
||||||
|
padding: 0,
|
||||||
|
color: 'inherit',
|
||||||
|
cursor: 'pointer',
|
||||||
|
textDecoration: 'underline',
|
||||||
|
font: 'inherit',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const params: string[] = []
|
||||||
|
if (subject) params.push(`subject=${encodeURIComponent(subject)}`)
|
||||||
|
if (body) params.push(`body=${encodeURIComponent(body)}`)
|
||||||
|
const href = `mailto:${addr}${params.length ? '?' + params.join('&') : ''}`
|
||||||
|
|
||||||
|
return <a href={href}>{addr}</a>
|
||||||
|
}
|
||||||
@ -90,7 +90,8 @@ function buildMailtoLink(payload: CheckoutPayload): string {
|
|||||||
`Total: ${fmtCents(payload.grandTotal)}`,
|
`Total: ${fmtCents(payload.grandTotal)}`,
|
||||||
].join('\n')
|
].join('\n')
|
||||||
|
|
||||||
return `mailto:info@beachpartyballoons.com?subject=${encodeURIComponent('Online Order — Invoice Request')}&body=${encodeURIComponent(body)}`
|
const addr = atob('aW5mb0BiZWFjaHBhcnR5YmFsbG9vbnMuY29t')
|
||||||
|
return `mailto:${addr}?subject=${encodeURIComponent('Online Order — Invoice Request')}&body=${encodeURIComponent(body)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PaymentForm({ payload, onSuccess, onError, active }: Props) {
|
export default function PaymentForm({ payload, onSuccess, onError, active }: Props) {
|
||||||
|
|||||||
@ -51,7 +51,17 @@
|
|||||||
<div class="is-flex-direction-column is-dark">
|
<div class="is-flex-direction-column is-dark">
|
||||||
<h2 class="is-size-4" style="text-align: center;"> <a target="_blank" href="https://maps.app.goo.gl/gRk6NztgMRUsSVJf9">554 Boston Post Road, Milford, CT 06460</a> </h2>
|
<h2 class="is-size-4" style="text-align: center;"> <a target="_blank" href="https://maps.app.goo.gl/gRk6NztgMRUsSVJf9">554 Boston Post Road, Milford, CT 06460</a> </h2>
|
||||||
<h2 class="is-size-4" style="text-align: center;" ><a href="tel:203.283.5626">203.283.5626</a> </h2>
|
<h2 class="is-size-4" style="text-align: center;" ><a href="tel:203.283.5626">203.283.5626</a> </h2>
|
||||||
<h2 class="is-size-4" style="text-align: center;" ><a href="mailto:info@beachpartyballoons.com">info@beachpartyballoons.com</a> </h2>
|
<h2 class="is-size-4" style="text-align: center;" id="bpb-email-h2">
|
||||||
|
<button id="bpb-email-btn" style="background:none;border:none;cursor:pointer;font:inherit;color:inherit;text-decoration:underline;padding:0;">
|
||||||
|
Click to show email address
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<script>
|
||||||
|
document.getElementById('bpb-email-btn').addEventListener('click', function () {
|
||||||
|
var a = atob('aW5mb0BiZWFjaHBhcnR5YmFsbG9vbnMuY29t');
|
||||||
|
document.getElementById('bpb-email-h2').innerHTML = '<a href="mailto:' + a + '">' + a + '<\/a>';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user