feat(org): add celebration confetti when joining or creating organization
Add a celebratory experience when users join or create an organization: - Add canvas-confetti dependency for visual effects - Store organization name in localStorage after successful join/create - Display celebration dialog with confetti animation on ProfilePage - Clear the celebration flag after showing to prevent repeat displays
This commit is contained in:
@@ -18,7 +18,21 @@ import {
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { api, ApiError, PendingInvite } from "@/lib/api";
|
||||
import { toast } from "@/hooks/use-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import confetti from "canvas-confetti";
|
||||
|
||||
// Wrapper to handle confetti failures gracefully
|
||||
const fireConfetti = () => {
|
||||
try {
|
||||
confetti({
|
||||
particleCount: 100,
|
||||
spread: 70,
|
||||
origin: { y: 0.6 },
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Confetti failed:', e);
|
||||
}
|
||||
};
|
||||
|
||||
function ProfileSkeleton() {
|
||||
return (
|
||||
@@ -68,6 +82,7 @@ function ProfileSkeleton() {
|
||||
export default function ProfilePage() {
|
||||
const { user, isLoading: authLoading, refreshUser, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [name, setName] = useState("");
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
@@ -79,6 +94,10 @@ export default function ProfilePage() {
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [confirmEmail, setConfirmEmail] = useState("");
|
||||
|
||||
// Celebration dialog state
|
||||
const [showCelebration, setShowCelebration] = useState(false);
|
||||
const [celebrationOrgName, setCelebrationOrgName] = useState("");
|
||||
|
||||
// Sync local name state with user data
|
||||
useEffect(() => {
|
||||
if (user?.full_name) {
|
||||
@@ -86,6 +105,17 @@ export default function ProfilePage() {
|
||||
}
|
||||
}, [user?.full_name]);
|
||||
|
||||
// Check for org creation/join celebration
|
||||
useEffect(() => {
|
||||
const fromStorage = localStorage.getItem('justJoinedOrg');
|
||||
if (fromStorage) {
|
||||
setCelebrationOrgName(fromStorage);
|
||||
setShowCelebration(true);
|
||||
fireConfetti();
|
||||
localStorage.removeItem('justJoinedOrg');
|
||||
}
|
||||
}, [location.pathname]);
|
||||
|
||||
// Fetch pending invitations for this user
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
@@ -477,6 +507,25 @@ export default function ProfilePage() {
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* Celebration dialog for org creation/join */}
|
||||
<Dialog open={showCelebration} onOpenChange={setShowCelebration}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2 text-center text-xl">
|
||||
🎉 Congratulations!
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-center text-base">
|
||||
You've joined <span className="font-semibold">{celebrationOrgName}</span>!
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex justify-center py-4">
|
||||
<Button onClick={() => setShowCelebration(false)}>
|
||||
Get Started
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user