Skip to content

[FEAT] Replace "Contact Us" email with meeting booking form(Cal)#66

Merged
idanlodzki merged 21 commits intoOpsiMate:mainfrom
Manishhhsys:feat/replace_contact_us_with_cal
Nov 5, 2025
Merged

[FEAT] Replace "Contact Us" email with meeting booking form(Cal)#66
idanlodzki merged 21 commits intoOpsiMate:mainfrom
Manishhhsys:feat/replace_contact_us_with_cal

Conversation

@Manishhhsys
Copy link
Contributor

@Manishhhsys Manishhhsys commented Oct 28, 2025

Issue Reference

SEE (#59 )

What Was Changed

  • Replaced email-based "Contact Us" section with an integrated meeting booking system
  • Implemented Cal.com booking widget across the website (navbar and contact section)
  • Added interactive calendar view for time slot selection

Why Was It Changed

To streamline the booking process and reduce friction. The new system allows instant meeting scheduling with real-time availability, eliminating back-and-forth emails and improving user experience.


Screenshots

cursorful-video-1761669353660.1.mp4

Note

In the Env file an new environment needs to be create under this name NEXT_PUBLIC_CAL_DATA_LINK and the value for this env variable is data-cal-link="manish-test-i3/30min this value can be found in the cal embed code just set

NEXT_PUBLIC_CAL_DATA_LINK=manish-test-i3/30min


Additional Context (Optional)

  • Integrated Cal.com with the following configuration:
    • data-cal-namespace="30min" for 30-minute meeting slots
    • Light theme with month view layout
    • Event type details visible for transparency
  • Environment variable NEXT_PUBLIC_CAL_DATA_LINK used for booking link configuration

Summary by CodeRabbit

  • New Features
    • Integrated appointment-booking CTA into navigation on desktop and mobile, replacing the previous contact link
    • Desktop CTA now includes an embedded logo and enhanced hover visuals
    • Mobile nav shows the booking CTA consistently with desktop behavior
    • Added scroll-responsive active-section highlighting and a subtle scroll blur for visual polish

@vercel
Copy link

vercel bot commented Oct 28, 2025

@Manishhhsys is attempting to deploy a commit to the idan lodzki's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Oct 28, 2025

Walkthrough

Replaced the Navbar Contact CTA with a Cal.com booking CTA, added Cal.com initialization and UI config in the Navbar, added section-highlight and scroll-blur behaviors, removed CONTACT_* exports, and updated package.json to add @calcom/embed-react and remove @tailwindcss/typography.

Changes

Cohort / File(s) Summary
CAL Booking & Navbar
components/Navbar.tsx
Imported Cal.com utilities and Next/Image; added env handling for NEXT_PUBLIC_CAL_DATA_LINK and created CAL_CONFIG/CAL_CONFIG_STRING; initialized Cal via getCalApi (namespace "30min") in useEffect; replaced previous Contact link with Cal booking CTAs (desktop & mobile) using data-cal-* attributes and embedded logo image; added IntersectionObserver active-section highlighting and scroll-based blur; removed exported CONTACT_EMAIL and CONTACT_MAILTO.
Dependencies
package.json
Added runtime dependency @calcom/embed-react; removed devDependency @tailwindcss/typography.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c077e6 and eccfe94.

📒 Files selected for processing (1)
  • components/Navbar.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/Navbar.tsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3125f5 and c521529.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • components/Navbar.tsx (4 hunks)
  • package.json (2 hunks)
🔇 Additional comments (5)
package.json (3)

22-22: Clarify @tailwindcss/typography status: summary vs. code mismatch.

The AI summary states that @tailwindcss/typography was "removed from devDependencies," but line 22 shows it still present. Please clarify whether this dependency is being removed, retained, or modified. If removed, verify that no components depend on typography utilities.


22-22: No action required—@tailwindcss/typography is retained.

The package is not being removed; it remains in the dependency list. The codebase uses prose utilities from this package across three pages (terms.tsx, privacy.tsx, blog/[id].tsx), and all dependencies are satisfied by keeping the package.


12-12: @calcom/embed-react@1.5.3 verified as compatible and secure.

@calcom/embed-react v1.5.3 is generally compatible with Next.js 14.2.30 and has no known direct security vulnerabilities. The package has peer dependencies pinned to React 18.2, which is compatible with Next.js 14.x. No action required.

components/Navbar.tsx (2)

1-8: LGTM! New imports are appropriate.

The added imports (useEffect, Plus, getCalApi, Image) are all utilized in the implementation and are appropriate for the Cal.com booking integration.


87-87: No action needed—the logo image path is valid.

The hardcoded path /images/logo.png correctly references the existing file at public/images/logo.png. The image will load without issues.

Likely an incorrect or invalid review comment.

Comment on lines 34 to 39
useEffect(() => {
(async function () {
const cal = await getCalApi({ "namespace": "30min" });
cal("ui", { "hideEventTypeDetails": true, "layout": "month_view" });
})();
}, [])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix configuration inconsistency and add error handling.

Two critical issues here:

  1. Configuration conflict: Line 37 sets hideEventTypeDetails: true, but both the desktop (line 83) and mobile (line 128) booking buttons set it to false. This inconsistency could cause unexpected behavior in the Cal.com widget.

  2. Missing error handling: If getCalApi fails, the booking functionality will silently break with no user feedback or fallback.

Apply this diff to resolve both issues:

  useEffect(() => {
    (async function () {
-      const cal = await getCalApi({ "namespace": "30min" });
-      cal("ui", { "hideEventTypeDetails": true, "layout": "month_view" });
+      try {
+        const cal = await getCalApi({ "namespace": "30min" });
+        cal("ui", { "hideEventTypeDetails": false, "layout": "month_view" });
+      } catch (error) {
+        console.error("Failed to initialize Cal.com:", error);
+      }
    })();
-  }, [])
+  }, []);
🤖 Prompt for AI Agents
In components/Navbar.tsx around lines 34-39, the Cal.com init lacks error
handling and its hideEventTypeDetails value conflicts with booking buttons; wrap
the async IIFE in a try/catch, await getCalApi inside the try, check the
returned cal before calling it, change hideEventTypeDetails to false to match
desktop/mobile buttons, and in the catch log the error (console.error or
existing logger) and optionally set a local state flag to disable booking
actions or show a user-facing fallback message.

@idanlodzki idanlodzki linked an issue Oct 29, 2025 that may be closed by this pull request
saksham-1304 and others added 2 commits October 30, 2025 21:34
- Add official Slack icon from lucide-react to all community links
- Update HeroSection.tsx with 20px Slack icon on CTA button
- Update CTASection.tsx with 20px Slack icon on secondary CTA
- Update Navbar.tsx with 18px Slack icon on navigation link
- Implement proper ARIA attributes (aria-label, aria-hidden) for accessibility
- Document icon usage conventions in .github/copilot-instructions.md

Enhances visual recognition and professional polish across all Slack
community touchpoints. Uses existing lucide-react dependency with zero
bundle size increase. All components maintain theme compatibility and
responsive behavior.

Technical details:
- Icon sizes: 20px for buttons, 18px for nav links
- Spacing: gap-2 (8px) between icon and text
- Accessibility: Screen readers announce 'Join our Slack community'
- Theme support: Icons use currentColor for auto light/dark adaptation
- Build verified: TypeScript + ESLint pass, production build successful
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
components/Navbar.tsx (1)

48-50: Keep the Cal.com UI config consistent with the button config.
Line 49 initializes Cal with hideEventTypeDetails: true, but Lines 24-27 define CAL_CONFIG with hideEventTypeDetails: false. This conflicting pair leads to unpredictable widget behavior. Align both paths on the same value (reusing CAL_CONFIG would be even better) so the UI state is deterministic.

-        const cal = await getCalApi({ "namespace": "30min" });
-        cal("ui", { "hideEventTypeDetails": true, "layout": "month_view" });
+        const cal = await getCalApi({ namespace: "30min" });
+        cal("ui", { hideEventTypeDetails: false, layout: "month_view" });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2697f5 and 9c0f8ea.

📒 Files selected for processing (3)
  • components/CTASection.tsx (2 hunks)
  • components/HeroSection.tsx (2 hunks)
  • components/Navbar.tsx (5 hunks)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
components/Navbar.tsx (1)

54-55: Align Cal UI config with button config

We initialise the Cal UI with hideEventTypeDetails: true, but every button requests hideEventTypeDetails: false. The conflict makes the embed behave inconsistently. Please keep the init value in sync with the per-button config.

-        const cal = await getCalApi({ "namespace": "30min" });
-        cal("ui", { "hideEventTypeDetails": true, "layout": "month_view" });
+        const cal = await getCalApi({ namespace: "30min" });
+        cal("ui", { hideEventTypeDetails: false, layout: "month_view" });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c0f8ea and 51bd940.

📒 Files selected for processing (6)
  • components/FeatureCard.tsx (2 hunks)
  • components/FeaturesSection.tsx (2 hunks)
  • components/Navbar.tsx (8 hunks)
  • components/PersonCard.tsx (1 hunks)
  • pages/about.tsx (1 hunks)
  • styles/globals.css (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
pages/about.tsx (1)
components/PersonCard.tsx (1)
  • Contributor (3-11)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
components/Navbar.tsx (2)

23-26: Still not preventing broken booking UI when env var is missing.

While environment variable validation was added, the booking buttons (lines 168 and 215) still render with data-cal-link={undefined} when NEXT_PUBLIC_CAL_DATA_LINK is not set. Users will see a "Book a Call" button that doesn't work.

Past reviews requested conditionally rendering the buttons based on whether calLink is present. Consider implementing the suggested isCalReady pattern from previous feedback.


51-60: Fix configuration inconsistency and guard initialization.

Three issues here:

  1. Configuration mismatch: Line 55 sets hideEventTypeDetails: true, but CAL_CONFIG (line 29) sets it to false. Choose one value and use it consistently.

  2. Missing guard: The initialization runs even when calLink is undefined. Add a check similar to the pattern suggested in previous reviews.

  3. Minor syntax: Missing semicolon after the closing bracket on line 60.

Apply this diff to address all issues:

  useEffect(() => {
+   if (!calLink) {
+     return;
+   }
    (async function () {
      try{
        const cal = await getCalApi({ "namespace": "30min" });
-       cal("ui", { "hideEventTypeDetails": true, "layout": "month_view" });
+       cal("ui", { "hideEventTypeDetails": false, "layout": "month_view" });
      }catch(error){
        console.error("Failed to initialize Cal.com:", error);
      }
    })();
- }, [])
+ }, [calLink]);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51bd940 and ff4403f.

📒 Files selected for processing (1)
  • components/Navbar.tsx (5 hunks)
🔇 Additional comments (4)
components/Navbar.tsx (4)

5-5: LGTM: Imports are correctly added.

The new imports (Plus icon, getCalApi, and Image) are properly used within the component for the Cal.com booking integration and UI enhancements.

Also applies to: 9-10


88-112: Duplicate detection - see comment on lines 63-86.

These useEffect blocks are duplicated from lines 63-86. Keep these and remove the earlier duplicates.


164-177: Desktop booking button implementation looks good.

The button uses semantic HTML, includes the extracted CAL_CONFIG_STRING, and has an engaging hover animation. The accessibility is adequate with a clear label.

The issue of rendering when calLink is undefined was already flagged in the comment on lines 23-26.


215-217: Mobile booking button correctly uses semantic HTML.

Good work using a button element instead of a div (as flagged in previous reviews). The implementation is clean and uses the extracted CAL_CONFIG_STRING.

The calLink undefined issue was already flagged earlier.

Comment on lines +63 to +86
useEffect(() => {
const sections = document.querySelectorAll("section[id]");
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveSection(entry.target.id);
}
});
},
{ rootMargin: "-50% 0px -50% 0px" }
);

sections.forEach((section) => observer.observe(section));
return () => sections.forEach((section) => observer.unobserve(section));
}, []);

// Blur effect on scroll
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > 20);
handleScroll();
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove duplicate scroll handling logic.

Lines 63-86 are completely duplicated at lines 88-112. This creates two IntersectionObservers and two scroll event listeners doing identical work, causing unnecessary performance overhead and potential race conditions in state updates.

Apply this diff to remove the duplication:

-  // Highlight active section on scroll
-  useEffect(() => {
-    const sections = document.querySelectorAll("section[id]");
-    const observer = new IntersectionObserver(
-      (entries) => {
-        entries.forEach((entry) => {
-          if (entry.isIntersecting) {
-            setActiveSection(entry.target.id);
-          }
-        });
-      },
-      { rootMargin: "-50% 0px -50% 0px" }
-    );
-
-    sections.forEach((section) => observer.observe(section));
-    return () => sections.forEach((section) => observer.unobserve(section));
-  }, []);
-
-  // Blur effect on scroll
-  useEffect(() => {
-    const handleScroll = () => setScrolled(window.scrollY > 20);
-    handleScroll();
-    window.addEventListener("scroll", handleScroll);
-    return () => window.removeEventListener("scroll", handleScroll);
-  }, []);
-
🤖 Prompt for AI Agents
In components/Navbar.tsx around lines 63-86, the scroll/section-observer logic
is duplicated (again at 88-112); remove the duplicated block so only one
useEffect creates the IntersectionObserver and one useEffect handles the scroll
blur, and ensure both effects correctly add and clean up their respective
observers/listeners (observer.observe/observer.unobserve and
window.addEventListener/removeEventListener) to avoid double observers/listeners
and redundant state updates.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/Navbar.tsx (1)

93-117: Remove duplicate useEffect hooks immediately.

Lines 93-117 duplicate the useEffect logic from lines 67-91, creating two IntersectionObservers and two scroll event listeners that both update the same state variables (activeSection and scrolled). This causes unnecessary performance overhead and potential race conditions.

Apply this diff to remove the duplication:

-  // Highlight active section on scroll
-  useEffect(() => {
-    const sections = document.querySelectorAll("section[id]");
-    const observer = new IntersectionObserver(
-      (entries) => {
-        entries.forEach((entry) => {
-          if (entry.isIntersecting) {
-            setActiveSection(entry.target.id);
-          }
-        });
-      },
-      { rootMargin: "-50% 0px -50% 0px" }
-    );
-
-    sections.forEach((section) => observer.observe(section));
-    return () => sections.forEach((section) => observer.unobserve(section));
-  }, []);
-
-  // Blur effect on scroll
-  useEffect(() => {
-    const handleScroll = () => setScrolled(window.scrollY > 20);
-    handleScroll();
-    window.addEventListener("scroll", handleScroll);
-    return () => window.removeEventListener("scroll", handleScroll);
-  }, []);
-
♻️ Duplicate comments (1)
components/Navbar.tsx (1)

169-182: Conditionally render desktop booking button based on isCalReady.

The desktop "Book a Call" button is always rendered, even when NEXT_PUBLIC_CAL_DATA_LINK is missing. This leaves users with a non-functional CTA. The mobile booking button (lines 223-229) is correctly gated with isCalReady — apply the same pattern here.

Apply this diff to conditionally render the desktop booking button:

            </Link>
            {/* Contact Us button */}
-            <button className="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 
-            duration-200 
-            flex items-center gap-2 flex-shrink-0
-            transition-all group"
-            data-cal-namespace="30min" data-cal-link={calLink} data-cal-config={CAL_CONFIG_STRING}>
-              <div className="flex items-center gap-2 group-hover:gap-8 transition-all duration-300 relative">
-              <Image src={'/images/logo.png'} width={28} height={28} alt="logo" className="rounded-full bg-white flex-shrink-0"></Image>
-              <div className="flex items-center gap-0 absolute left-[28px] transform -translate-x-full opacity-0 group-hover:translate-x-0 group-hover:opacity-100 transition-all duration-300">     
-                <div className="w-5 h-5 rounded-full flex items-center justify-center text-[10px] ml-1 mr-1"><Plus width={20} height={20}></Plus></div>
-                <div className="w-7 h-7 rounded-full dark:bg-white/10 bg-white/20 flex items-center justify-center text-[10px] ml-1 mr-1">You</div>
-              </div>
-              <span className="whitespace-nowrap relative block text-base font-bold ml-0 group-hover:ml-10 transition-all duration-300">Book a Call</span>
-            </div>  
-            </button>
+            {isCalReady && (
+              <button className="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 
+              duration-200 
+              flex items-center gap-2 flex-shrink-0
+              transition-all group"
+              data-cal-namespace="30min" data-cal-link={calLink} data-cal-config={CAL_CONFIG_STRING}>
+                <div className="flex items-center gap-2 group-hover:gap-8 transition-all duration-300 relative">
+                <Image src={'/images/logo.png'} width={28} height={28} alt="logo" className="rounded-full bg-white flex-shrink-0"></Image>
+                <div className="flex items-center gap-0 absolute left-[28px] transform -translate-x-full opacity-0 group-hover:translate-x-0 group-hover:opacity-100 transition-all duration-300">     
+                  <div className="w-5 h-5 rounded-full flex items-center justify-center text-[10px] ml-1 mr-1"><Plus width={20} height={20}></Plus></div>
+                  <div className="w-7 h-7 rounded-full dark:bg-white/10 bg-white/20 flex items-center justify-center text-[10px] ml-1 mr-1">You</div>
+                </div>
+                <span className="whitespace-nowrap relative block text-base font-bold ml-0 group-hover:ml-10 transition-all duration-300">Book a Call</span>
+              </div>  
+              </button>
+            )}
            <ThemeToggle />
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff4403f and 0c077e6.

📒 Files selected for processing (1)
  • components/Navbar.tsx (5 hunks)
🔇 Additional comments (3)
components/Navbar.tsx (3)

23-35: Environment validation and config extraction look good.

The environment variable validation with trimming and readiness checks, along with the extracted CAL_CONFIG constant, properly address previous review concerns. The configuration is now centralized and consistently applied.


53-65: Cal.com initialization properly implemented with guards and error handling.

The initialization correctly checks isCalReady before proceeding, includes try-catch for graceful failure, and has the proper dependency array. This addresses previous review feedback effectively.


223-229: Mobile booking button properly guarded.

The mobile "Book A Call" button is correctly wrapped in the isCalReady check, preventing a non-functional CTA from appearing when the environment variable is missing. This is the correct pattern.

@idanlodzki idanlodzki merged commit b8dbed1 into OpsiMate:main Nov 5, 2025
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[UI] Replace "Contact Us" email with meeting booking form

5 participants