Merge pull request #113 from Hestia-Homes/feature/book-a-survey-with-information-of-contact

Feature/book a survey with information of contact
This commit is contained in:
KhalimCK 2025-11-03 19:13:17 +00:00 committed by GitHub
commit 040b13dd57
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 199 additions and 51 deletions

View file

@ -1,40 +1,33 @@
// app/api/book-survey/route.ts
import { NextResponse } from "next/server";
import { db } from "@/app/db/db";
import { propertyStatusTracker } from "@/app/db/schema/crm/property_status_tracker";
import { eq, and } from "drizzle-orm";
import { user } from "@/app/db/schema/users";
export async function POST(req: Request) {
console.log("📩 Incoming POST /api/property-status request");
try {
const { dealName, pipelineId, dealStageId, propertyId, portfolioId } =
await req.json();
const {
pipelineId,
dealStageId,
propertyId,
portfolioId,
userInfo,
propertyMeta,
} = await req.json();
// 1⃣ Create HubSpot deal
const hsRes = await fetch("https://api.hubapi.com/crm/v3/objects/deals", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.HUBSPOT_API_KEY}`,
},
body: JSON.stringify({
properties: {
dealname: dealName,
pipeline: pipelineId,
dealstage: dealStageId,
},
}),
console.log("🧠 Parsed body:", {
pipelineId,
dealStageId,
propertyId,
portfolioId,
userInfo,
propertyMeta,
});
if (!hsRes.ok) {
const err = await hsRes.text();
throw new Error(`HubSpot error: ${err}`);
}
const hsData = await hsRes.json();
const hubspotDealId = hsData.id;
// 2⃣ Check if record exists for property + portfolio
// 1⃣ Check if record exists first
console.log("🔍 Checking if record already exists in DB...");
const existing = await db
.select()
.from(propertyStatusTracker)
@ -45,32 +38,170 @@ export async function POST(req: Request) {
)
);
console.log("🗃️ Existing record check result:", existing);
if (existing.length > 0) {
// 3⃣ Update existing record
await db
.update(propertyStatusTracker)
.set({
hubspotDealId,
updatedAt: new Date(),
})
.where(
and(
eq(propertyStatusTracker.propertyId, propertyId),
eq(propertyStatusTracker.portfolioId, portfolioId)
)
);
} else {
// 4⃣ Create new record
await db.insert(propertyStatusTracker).values({
hubspotDealId: hubspotDealId,
propertyId: propertyId,
portfolioId: portfolioId,
console.log("⚠️ Record already exists, skipping deal creation");
return NextResponse.json({
message: "Record already exists, no new deal created",
dealId: existing[0].hubspotDealId,
});
}
// 2⃣ Create HubSpot deal
console.log("🧱 Creating HubSpot deal...");
const dealRes = await fetch("https://api.hubapi.com/crm/v3/objects/deals", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.HUBSPOT_API_KEY}`,
},
body: JSON.stringify({
properties: {
dealname: propertyMeta?.address || "New Property Deal",
pipeline: pipelineId,
dealstage: dealStageId,
},
}),
});
console.log("📡 HubSpot deal response status:", dealRes.status);
if (!dealRes.ok) {
const err = await dealRes.text();
console.error("❌ HubSpot Deal creation failed:", err);
throw new Error(`HubSpot Deal Error: ${err}`);
}
const dealData = await dealRes.json();
const hubspotDealId = dealData.id;
console.log("✅ Created HubSpot deal:", hubspotDealId);
// 3⃣ Retrieve user info from your DB
console.log("👤 Fetching user info from DB...");
const userProfile = await db
.select()
.from(user)
.where(eq(user.id, userInfo.dbId))
.limit(1);
const userInfoFromDb = userProfile[0];
console.log("📇 User info from DB:", userInfoFromDb);
if (!userInfoFromDb?.email) {
console.error("❌ User email missing in DB for user:", userInfo.dbId);
throw new Error("User email not found; cannot create HubSpot contact.");
}
// 4⃣ Create or find contact in HubSpot
console.log("📞 Creating HubSpot contact...");
const contactRes = await fetch("https://api.hubapi.com/crm/v3/objects/contacts", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.HUBSPOT_API_KEY}`,
},
body: JSON.stringify({
properties: {
email: userInfoFromDb.email,
},
}),
});
let hubspotContactId: string | null = null;
console.log("📡 HubSpot contact response status:", contactRes.status);
if (contactRes.ok) {
const contactData = await contactRes.json();
hubspotContactId = contactData.id;
console.log("✅ Created new HubSpot contact:", hubspotContactId);
} else {
console.warn("⚠️ HubSpot contact creation failed — checking if contact exists...");
// Check if contact already exists
const findContactRes = await fetch(
`https://api.hubapi.com/crm/v3/objects/contacts/search`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.HUBSPOT_API_KEY}`,
},
body: JSON.stringify({
filterGroups: [
{
filters: [
{
propertyName: "email",
operator: "EQ",
value: userInfoFromDb.email,
},
],
},
],
}),
}
);
console.log("📡 HubSpot contact search response:", findContactRes.status);
if (findContactRes.ok) {
const found = await findContactRes.json();
console.log("🔎 Found contact results:", found.results);
if (found.results?.length > 0) {
hubspotContactId = found.results[0].id;
console.log("✅ Found existing HubSpot contact:", hubspotContactId);
} else {
console.error("❌ HubSpot contact creation and lookup both failed");
throw new Error("HubSpot contact creation and lookup both failed.");
}
} else {
const findErr = await findContactRes.text();
console.error("❌ HubSpot contact search failed:", findErr);
throw new Error("HubSpot contact lookup request failed.");
}
}
// 5⃣ Associate contact with deal
if (hubspotContactId) {
console.log("🔗 Associating HubSpot deal and contact...");
const assocUrl = `https://api.hubapi.com/crm/v3/objects/deals/${hubspotDealId}/associations/contacts/${hubspotContactId}/deal_to_contact`;
const assocRes = await fetch(assocUrl, {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.HUBSPOT_API_KEY}`,
"Content-Type": "application/json",
},
});
console.log("📡 HubSpot association response:", assocRes.status);
if (!assocRes.ok) {
const assocErr = await assocRes.text();
console.warn("⚠️ HubSpot association failed:", assocErr);
} else {
console.log("✅ Successfully associated contact with deal");
}
}
// 6⃣ Create DB record
console.log("🗄️ Inserting new tracker record into DB...");
await db.insert(propertyStatusTracker).values({
hubspotDealId,
propertyId,
portfolioId,
createdAt: new Date(),
updatedAt: new Date(),
});
console.log("✅ All done — returning success response");
return NextResponse.json({
message: existing.length > 0 ? "Updated existing tracker" : "Created new tracker",
message: "Created new tracker, HubSpot deal, and linked contact",
dealId: hubspotDealId,
contactId: hubspotContactId,
});
} catch (error: any) {
console.error("❌ Error creating or updating HubSpot deal:", error);

View file

@ -70,6 +70,11 @@ export function Toolbar({
const [openModal, setOpenModal] = useState(false);
const [showToast, setShowToast] = useState(false);
console.log(propertyId, "PropertyID")
console.log(portfolioId, "porfolio id")
console.log(propertyMeta, "property meta")
console.log(decentHomes, "decent homes")
function handleClickSettings() {
console.log("Settings were clicked, implement me");
}
@ -175,7 +180,7 @@ export function Toolbar({
onOpenChange={setOpenModal}
propertyId={BigInt(propertyId)}
portfolioId={portfolioId}
address={propertyMeta.address}
propertyMeta={propertyMeta}
onSuccess={() => setShowToast(true)}
/>
)}

View file

@ -27,6 +27,7 @@ export default async function DashboardLayout(props: {
const propertyId = params.propertyId ?? "";
const portfolioId = params.slug ?? "";
// The layout is a server component by default so we can fetch meta data here
const propertyMeta = await getPropertyMeta(params.propertyId);

View file

@ -12,24 +12,34 @@ import { Input } from "@/app/shadcn_components/ui/input";
import { Label } from "@/app/shadcn_components/ui/label";
import { useState, useEffect } from "react";
import { useMutation } from "@tanstack/react-query";
import { PropertyMeta } from "@/app/db/schema/property";
import { cache } from "react";
import { useSession } from "next-auth/react";
interface BookSurveyModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
propertyId: bigint;
portfolioId: string;
address: string;
propertyMeta: PropertyMeta;
onSuccess?: () => void; // ✅ fix: properly declare optional callback
}
export default function BookSurveyModal({
open,
onOpenChange,
propertyId,
portfolioId,
address,
propertyMeta,
onSuccess, // ✅ fix: remove “?:” here, we already declared it optional in interface
}: BookSurveyModalProps) {
const { data: session, status } = useSession();
const user = session?.user;
// 🧠 Simple mutation to call your HubSpot API
const bookSurveyMutation = useMutation({
mutationFn: async () => {
@ -37,11 +47,12 @@ export default function BookSurveyModal({
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
dealName: address,
pipelineId: "2400089278",
dealStageId: "3660660975",
propertyId: propertyId.toString(),
portfolioId: portfolioId,
userInfo: user,
propertyMeta: propertyMeta,
}),
});