mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
added fun confettit
This commit is contained in:
parent
5772f64f7a
commit
84413674c7
5 changed files with 154 additions and 0 deletions
65
package-lock.json
generated
65
package-lock.json
generated
|
|
@ -42,6 +42,7 @@
|
|||
"drizzle-orm": "^0.44.5",
|
||||
"esbuild": "^0.25.8",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"framer-motion": "^12.23.24",
|
||||
"lucide-react": "^0.233.0",
|
||||
"next": "^15.4.2",
|
||||
"next-auth": "^4.22.1",
|
||||
|
|
@ -50,6 +51,7 @@
|
|||
"pg": "^8.11.1",
|
||||
"postcss": "^8.5.6",
|
||||
"react": "18.3.1",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-dom": "18.3.1",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"tailwind-merge": "^1.13.2",
|
||||
|
|
@ -9615,6 +9617,33 @@
|
|||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.23.24",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz",
|
||||
"integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.23",
|
||||
"motion-utils": "^12.23.6",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/from": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
|
||||
|
|
@ -11292,6 +11321,21 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.23.23",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.6"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
|
@ -12656,6 +12700,21 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-confetti": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-confetti/-/react-confetti-6.4.0.tgz",
|
||||
"integrity": "sha512-5MdGUcqxrTU26I2EU7ltkWPwxvucQTuqMm8dUz72z2YMqTD6s9vMcDUysk7n9jnC+lXuCPeJJ7Knf98VEYE9Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tween-functions": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.3.0 || ^17.0.1 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-day-picker": {
|
||||
"version": "8.10.1",
|
||||
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz",
|
||||
|
|
@ -14372,6 +14431,12 @@
|
|||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/tween-functions": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz",
|
||||
"integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==",
|
||||
"license": "BSD"
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
"drizzle-orm": "^0.44.5",
|
||||
"esbuild": "^0.25.8",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"framer-motion": "^12.23.24",
|
||||
"lucide-react": "^0.233.0",
|
||||
"next": "^15.4.2",
|
||||
"next-auth": "^4.22.1",
|
||||
|
|
@ -56,6 +57,7 @@
|
|||
"pg": "^8.11.1",
|
||||
"postcss": "^8.5.6",
|
||||
"react": "18.3.1",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-dom": "18.3.1",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"tailwind-merge": "^1.13.2",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ interface BookSurveyModalProps {
|
|||
propertyId: bigint;
|
||||
portfolioId: bigint;
|
||||
address: string;
|
||||
onSuccess?: () => void; // ✅ fix: properly declare optional callback
|
||||
}
|
||||
|
||||
export default function BookSurveyModal({
|
||||
|
|
@ -27,6 +28,7 @@ export default function BookSurveyModal({
|
|||
propertyId,
|
||||
portfolioId,
|
||||
address,
|
||||
onSuccess, // ✅ fix: remove “?:” here, we already declared it optional in interface
|
||||
}: BookSurveyModalProps) {
|
||||
const [formData, setFormData] = useState({
|
||||
dealname: "",
|
||||
|
|
@ -54,6 +56,7 @@ export default function BookSurveyModal({
|
|||
console.log("✅ Deal created successfully:", data);
|
||||
console.log("HUBSPOT DEAL ID MADE", data.dealId);
|
||||
onOpenChange(false);
|
||||
if (onSuccess) onSuccess(); // 👈 trigger confetti toast
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("❌ Deal creation failed:", error);
|
||||
|
|
@ -119,5 +122,12 @@ export default function BookSurveyModal({
|
|||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// TODO: check if bug is happening and why.
|
||||
// TODO: Ask khalim what we want to do, maybe a list of Hubspot DB record? if someone presses twice, currently just updates
|
||||
// TODO: Make a sexy toast that the deal has been processed
|
||||
// TODO: Show khalim a demo and other clean ups for good user experience
|
||||
66
src/app/portfolio/[slug]/components/BookingSuccessToast.tsx
Normal file
66
src/app/portfolio/[slug]/components/BookingSuccessToast.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import Confetti from "react-confetti";
|
||||
import { CheckCircle } from "lucide-react";
|
||||
|
||||
interface BookingSuccessToastProps {
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
message?: string;
|
||||
subtext?: string;
|
||||
}
|
||||
|
||||
export default function BookingSuccessToast({
|
||||
show,
|
||||
onClose,
|
||||
message = "Booking Confirmed!",
|
||||
subtext = "You’re all set. 🎉",
|
||||
}: BookingSuccessToastProps) {
|
||||
const [confetti, setConfetti] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
setConfetti(true);
|
||||
const timer = setTimeout(() => {
|
||||
setConfetti(false);
|
||||
onClose();
|
||||
}, 4000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [show, onClose]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimatePresence>
|
||||
{show && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 50, scale: 0.9 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: 50, scale: 0.9 }}
|
||||
transition={{ type: "spring", stiffness: 300, damping: 25 }}
|
||||
className="fixed bottom-8 right-8 z-50 bg-white shadow-2xl rounded-2xl p-4 pr-6 flex items-center gap-3 border border-green-100"
|
||||
>
|
||||
<div className="p-2 bg-green-100 rounded-full">
|
||||
<CheckCircle className="text-green-600 w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-green-700 text-lg">{message}</p>
|
||||
<p className="text-sm text-gray-500">{subtext}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{confetti && (
|
||||
<Confetti
|
||||
numberOfPieces={180}
|
||||
gravity={0.4}
|
||||
recycle={false}
|
||||
colors={["#10B981", "#34D399", "#6EE7B7", "#ECFDF5"]}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import {
|
|||
PropertyWithRelations,
|
||||
} from "@/app/db/schema/property";
|
||||
import BookSurveyModal from "./BookSurveyModal";
|
||||
import BookingSuccessToast from "./BookingSuccessToast";
|
||||
import { useState } from "react";
|
||||
|
||||
|
||||
|
|
@ -218,6 +219,7 @@ export const columns: ColumnDef<PropertyWithRelations>[] = [
|
|||
id: "actions",
|
||||
cell: ({ row }) => {
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [showToast, setShowToast] = useState(false);
|
||||
const property = row.original;
|
||||
const propertyId = property.id;
|
||||
const portfolioId = property.portfolioId;
|
||||
|
|
@ -271,8 +273,17 @@ export const columns: ColumnDef<PropertyWithRelations>[] = [
|
|||
propertyId={propertyId}
|
||||
portfolioId={portfolioId}
|
||||
address={"ask khalim"}
|
||||
onSuccess={() => setShowToast(true)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 💥 Toast */}
|
||||
<BookingSuccessToast
|
||||
show={showToast}
|
||||
onClose={() => setShowToast(false)}
|
||||
message="Deal Created Successfully!"
|
||||
subtext="Your HubSpot deal is ready. 🎉"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue