feat: integrate Cal.com popup booking via @calcom/embed-react
All checks were successful
Build & Push / build-and-push (push) Successful in 4m52s
All checks were successful
Build & Push / build-and-push (push) Successful in 4m52s
- CalButton component: loads Cal.com embed script, triggers popup on click - Configured for scheduling.letsbe.solutions (self-hosted) - Wired into configurator completion step (Book a Call) - Wired into footer Book a Call button - Brand colors: #006494 light, #5BA4D9 dark Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
31
package-lock.json
generated
31
package-lock.json
generated
@@ -7,8 +7,8 @@
|
||||
"": {
|
||||
"name": "letsbe-agency",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@calcom/embed-react": "^1.5.3",
|
||||
"@payloadcms/db-postgres": "^3.80.0",
|
||||
"@payloadcms/next": "^3.80.0",
|
||||
"@payloadcms/richtext-lexical": "^3.80.0",
|
||||
@@ -215,6 +215,35 @@
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/@calcom/embed-core": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@calcom/embed-core/-/embed-core-1.5.3.tgz",
|
||||
"integrity": "sha512-GeId9gaByJ5EWiPmuvelZOvFWPOTWkcWZr5vGTCbIUTX125oE5yn0n8lDF1MJk5Xj1WO+/dk9jKIE08Ad9ytiQ==",
|
||||
"license": "SEE LICENSE IN LICENSE"
|
||||
},
|
||||
"node_modules/@calcom/embed-react": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@calcom/embed-react/-/embed-react-1.5.3.tgz",
|
||||
"integrity": "sha512-JCgge04pc8fhdvUmPNVLhW8/lCWK+AAziKecKWWPfv1nn2s+qKP2BwsEAnxhxK9yPOBgE1EIEgmYkrrNB1iajA==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@calcom/embed-core": "1.5.3",
|
||||
"@calcom/embed-snippet": "1.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@calcom/embed-snippet": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@calcom/embed-snippet/-/embed-snippet-1.3.3.tgz",
|
||||
"integrity": "sha512-pqqKaeLB8R6BvyegcpI9gAyY6Xyx1bKYfWvIGOvIbTpguWyM1BBBVcT9DCeGe8Zw7Ujp5K56ci7isRUrT2Uadg==",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@calcom/embed-core": "1.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@date-fns/tz": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"generate:types": "payload generate:types"
|
||||
},
|
||||
"dependencies": {
|
||||
"@calcom/embed-react": "^1.5.3",
|
||||
"@payloadcms/db-postgres": "^3.80.0",
|
||||
"@payloadcms/next": "^3.80.0",
|
||||
"@payloadcms/richtext-lexical": "^3.80.0",
|
||||
|
||||
@@ -5,6 +5,7 @@ import { motion } from 'framer-motion';
|
||||
import { Calendar, Mail } from 'lucide-react';
|
||||
import AnimatedCheckmark from '@/components/icons/AnimatedCheckmark';
|
||||
import Button from '@/components/ui/Button';
|
||||
import CalButton from '@/components/ui/CalButton';
|
||||
import type { WizardFormData } from './WizardContainer';
|
||||
|
||||
// ─── Brief Renderer ───────────────────────────────────────────────────────────
|
||||
@@ -70,26 +71,8 @@ function renderBrief(brief: string) {
|
||||
// ─── Cal.com Embed / Booking ──────────────────────────────────────────────────
|
||||
|
||||
function BookingSection() {
|
||||
const calcomUrl = process.env.NEXT_PUBLIC_CALCOM_URL;
|
||||
|
||||
if (calcomUrl) {
|
||||
return (
|
||||
<div className="rounded-xl overflow-hidden border border-outline-variant/40 bg-surface-high">
|
||||
<iframe
|
||||
src={calcomUrl}
|
||||
width="100%"
|
||||
height="480"
|
||||
frameBorder="0"
|
||||
title="Book a consultation"
|
||||
className="block"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-outline-variant/40 bg-surface-low px-5 py-5 text-center">
|
||||
<div className="rounded-xl bg-surface-low px-5 py-5 text-center">
|
||||
<div className="flex justify-center mb-3">
|
||||
<span className="w-10 h-10 rounded-xl bg-primary/10 flex items-center justify-center">
|
||||
<Calendar size={18} strokeWidth={1.5} className="text-primary-dark" />
|
||||
@@ -97,16 +80,13 @@ function BookingSection() {
|
||||
</div>
|
||||
<p className="text-sm font-semibold text-on-surface mb-1">Book a Consultation</p>
|
||||
<p className="text-xs text-outline mb-4">30 minutes to discuss your brief with our team</p>
|
||||
<Button
|
||||
variant="primary"
|
||||
href="https://cal.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
size="sm"
|
||||
arrow
|
||||
<CalButton
|
||||
className="inline-flex items-center gap-2 px-6 py-2.5 rounded-lg text-sm font-medium text-white transition-all hover:-translate-y-px active:translate-y-0"
|
||||
style={{ background: 'linear-gradient(135deg, #006494, #5BA4D9)' }}
|
||||
>
|
||||
<Calendar size={16} />
|
||||
Book a Call
|
||||
</Button>
|
||||
</CalButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useTranslations } from 'next-intl'
|
||||
'use client'
|
||||
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { Link } from '@/i18n/navigation'
|
||||
import CalButton from '@/components/ui/CalButton'
|
||||
|
||||
// ── Static link data ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -180,18 +182,12 @@ export default function Footer() {
|
||||
</ul>
|
||||
|
||||
{/* Book a Call CTA */}
|
||||
<a
|
||||
href={CAL_LINK}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-full label-md transition-all duration-300 hover:scale-[1.03] hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #006494, #5BA4D9)',
|
||||
color: '#fff',
|
||||
}}
|
||||
<CalButton
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-full label-md text-white transition-all duration-300 hover:scale-[1.03] hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
|
||||
style={{ background: 'linear-gradient(135deg, #006494, #5BA4D9)' }}
|
||||
>
|
||||
{tNav('bookCall')}
|
||||
</a>
|
||||
</CalButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
44
src/components/ui/CalButton.tsx
Normal file
44
src/components/ui/CalButton.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
'use client'
|
||||
|
||||
import { getCalApi } from '@calcom/embed-react'
|
||||
import { useEffect } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const CAL_ORIGIN = 'https://scheduling.letsbe.solutions'
|
||||
const CAL_LINK = 'matt-ciaccio/letsbe'
|
||||
const CAL_NAMESPACE = 'letsbe'
|
||||
|
||||
interface CalButtonProps {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export default function CalButton({ children, className, style }: CalButtonProps) {
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
const cal = await getCalApi({ namespace: CAL_NAMESPACE })
|
||||
cal('init', CAL_NAMESPACE, { origin: CAL_ORIGIN })
|
||||
cal('ui', {
|
||||
hideEventTypeDetails: false,
|
||||
layout: 'month_view',
|
||||
cssVarsPerTheme: {
|
||||
light: { 'cal-brand': '#006494' },
|
||||
dark: { 'cal-brand': '#5BA4D9' },
|
||||
},
|
||||
})
|
||||
})()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<button
|
||||
data-cal-link={CAL_LINK}
|
||||
data-cal-namespace={CAL_NAMESPACE}
|
||||
data-cal-config='{"layout":"month_view","useSlotsViewOnSmallScreen":"true"}'
|
||||
className={cn(className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user