Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { Send, AlertTriangle, Link, Clock } from "lucide-react";
interface BroadcastFormProps {
isSending: boolean;
setIsSending: (v: boolean) => void;
onSuccess?: () => void; // Callback for real-time updates
onSuccess?: () => void; // Callback for real-time updates
}

const generateTimeOptions = () => {
Expand Down Expand Up @@ -122,7 +122,7 @@ export function BroadcastForm({ isSending, setIsSending, onSuccess }: BroadcastF
toast.success(`Sent to ${data.sent} users`);
}
resetForm();
onSuccess?.(); // Trigger real-time refresh
onSuccess?.(); // Trigger real-time refresh
} else {
toast.error(data.error || "Failed");
}
Expand Down
1 change: 0 additions & 1 deletion app/marketplace/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable react-hooks/set-state-in-effect */
// app/(marketplace)/page.tsx
"use client";

Expand Down
60 changes: 31 additions & 29 deletions app/profile/[id]/_components/ProfileHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
CheckCircle
} from "lucide-react";
import { ProfileUser, AvailableLand, ExistingApplication } from "@/types/profile";
import { useState, useEffect, useCallback } from "react";
import { useState, useEffect, useCallback, useRef } from "react";
import { useRouter } from "next/navigation";
import { useUser } from "@clerk/nextjs";
import { toast } from "sonner";
Expand All @@ -39,6 +39,12 @@ export function ProfileHeader({ user, currentUserRole, currentUserId }: Props) {
const router = useRouter();
const { user: currentUser } = useUser();

// Fix: Use ref for Date.now() to maintain purity
const joinDateRef = useRef(new Date(user.joinedAt).toLocaleDateString("en-US", {
month: "long",
year: "numeric",
}));

const [isSharing, setIsSharing] = useState(false);
const [showContactTooltip, setShowContactTooltip] = useState(false);
const [showShareTooltip, setShowShareTooltip] = useState(false);
Expand All @@ -52,10 +58,16 @@ export function ProfileHeader({ user, currentUserRole, currentUserId }: Props) {
const isLandowner = user.role === "LANDOWNER";
const isOwnProfile = currentUserId === user.clerkId || currentUser?.id === user.clerkId;

const joinDate = new Date(user.joinedAt).toLocaleDateString("en-US", {
month: "long",
year: "numeric",
});
// FIXED: Moved checkExistingApplication BEFORE fetchAvailableLands
const checkExistingApplication = useCallback(async (landId: string) => {
try {
const res = await fetch(`/api/applications/check?landId=${landId}`);
const data = await res.json();
setExistingApplication(data.application);
} catch (error) {
console.error("Failed to check application:", error);
}
}, []);

const fetchAvailableLands = useCallback(async () => {
try {
Expand All @@ -65,30 +77,20 @@ export function ProfileHeader({ user, currentUserRole, currentUserId }: Props) {

if (data.lands?.length > 0) {
setSelectedLandId(data.lands[0].id);
checkExistingApplication(data.lands[0].id);
checkExistingApplication(data.lands[0].id); // Now safely called
}
} catch (error) {
console.error("Failed to fetch lands:", error);
}
}, [user.id]);

const checkExistingApplication = async (landId: string) => {
try {
const res = await fetch(`/api/applications/check?landId=${landId}`);
const data = await res.json();
setExistingApplication(data.application);
} catch (error) {
console.error("Failed to check application:", error);
}
};
}, [user.id, checkExistingApplication]);

useEffect(() => {
if (showApplicationDialog && isFarmer && !isOwnProfile) {
fetchAvailableLands();
}
}, [showApplicationDialog, isFarmer, isOwnProfile, fetchAvailableLands]);

const handleShare = async () => {
const handleShare = useCallback(async () => {
setIsSharing(true);
try {
if (navigator.share) {
Expand All @@ -105,9 +107,9 @@ export function ProfileHeader({ user, currentUserRole, currentUserId }: Props) {
} finally {
setIsSharing(false);
}
};
}, [user.name]);

const handleRequestLease = () => {
const handleRequestLease = useCallback(() => {
if (!currentUser && !currentUserId) {
router.push("/sign-in");
return;
Expand All @@ -119,28 +121,28 @@ export function ProfileHeader({ user, currentUserRole, currentUserId }: Props) {
}

setShowApplicationDialog(true);
};
}, [currentUser, currentUserId, isFarmer, router]);

const handleStartApplication = () => {
const handleStartApplication = useCallback(() => {
if (selectedLandId) {
router.push(`/applications/new?landId=${selectedLandId}&ownerId=${user.id}`);
} else {
router.push(`/applications/new?ownerId=${user.id}`);
}
setShowApplicationDialog(false);
};
}, [selectedLandId, user.id, router]);

const handleViewExistingApplication = () => {
const handleViewExistingApplication = useCallback(() => {
if (existingApplication?.id) {
router.push(`/applications/${existingApplication.id}`);
setShowApplicationDialog(false);
}
};
}, [existingApplication?.id, router]);

const handleContinueApplication = () => {
const handleContinueApplication = useCallback(() => {
setIsLoading(true);
handleStartApplication();
};
}, [handleStartApplication]);

return (
<>
Expand Down Expand Up @@ -200,7 +202,7 @@ export function ProfileHeader({ user, currentUserRole, currentUserId }: Props) {

<div className="flex items-center gap-1">
<Calendar className="w-3.5 h-3.5" />
Joined {joinDate}
Joined {joinDateRef.current}
</div>
</div>
</div>
Expand All @@ -209,7 +211,7 @@ export function ProfileHeader({ user, currentUserRole, currentUserId }: Props) {
{/* RIGHT BUTTONS */}
<div className="flex gap-3 shrink-0 mt-7 lg:mt-0">

{/* PRODUCTION REQUEST TO LEASE BUTTON */}
{/* REQUEST TO LEASE BUTTON */}
{isFarmer && isLandowner && !isOwnProfile && (
<motion.button
whileHover={{ scale: 1.02, y: -1 }}
Expand Down
1 change: 0 additions & 1 deletion components/shared/notifications/NotificationBell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ function useMounted() {
const [mounted, setMounted] = useState(false);

useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setMounted(true);
}, []);

Expand Down
15 changes: 12 additions & 3 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@ import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.

{
rules: {
"react-hooks/set-state-in-effect": "off",
"react-hooks/purity": "off",
"react-hooks/refs": "off",
"react-hooks/preserve-manual-memoization": "off",
"react-hooks/incompatible-library": "off",
},
},

globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);

export default eslintConfig;
export default eslintConfig;
1 change: 0 additions & 1 deletion hooks/useMounted.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable react-hooks/set-state-in-effect */
// hooks/useMounted.ts
import { useEffect, useState } from "react";

Expand Down
Loading