feat: Implement agent availability status with a toggle for agents and display on user profiles.

This commit is contained in:
pradeepkumar
2026-02-02 00:34:31 +05:30
parent b88e47f911
commit edfbf4a51c
4 changed files with 102 additions and 18 deletions

View File

@@ -109,6 +109,8 @@ export default function AgentDashboard() {
const [isPhotoModalOpen, setIsPhotoModalOpen] = useState(false); const [isPhotoModalOpen, setIsPhotoModalOpen] = useState(false);
const [imageError, setImageError] = useState(false); const [imageError, setImageError] = useState(false);
const [avatarUrl, setAvatarUrl] = useState<string | null>(null); const [avatarUrl, setAvatarUrl] = useState<string | null>(null);
const [isAvailable, setIsAvailable] = useState(true);
const [isTogglingAvailability, setIsTogglingAvailability] = useState(false);
const defaultImage = '/assets/demo_images/8f017153a7b4a239a4f0691234ef97dd55092282.jpg'; const defaultImage = '/assets/demo_images/8f017153a7b4a239a4f0691234ef97dd55092282.jpg';
@@ -134,6 +136,7 @@ export default function AgentDashboard() {
setAgentProfile(profile); setAgentProfile(profile);
setFieldValues(fieldValuesResponse.fieldValues); setFieldValues(fieldValuesResponse.fieldValues);
setIsAvailable(profile.isAvailable ?? true);
// Map field values to experience data structure // Map field values to experience data structure
const mappedExperience = mapFieldValuesToExperience(fieldValuesResponse.fieldValues); const mappedExperience = mapFieldValuesToExperience(fieldValuesResponse.fieldValues);
@@ -175,6 +178,28 @@ export default function AgentDashboard() {
fetchData(); fetchData();
}, []); }, []);
// Handle availability toggle
const handleToggleAvailability = async () => {
if (isTogglingAvailability) return;
try {
setIsTogglingAvailability(true);
const newAvailability = !isAvailable;
// Update on server
const updatedProfile = await agentsService.updateProfile({ isAvailable: newAvailability });
// Update local state
setIsAvailable(newAvailability);
setAgentProfile(updatedProfile);
} catch (err) {
console.error('Failed to update availability:', err);
// Revert on error - state stays the same
} finally {
setIsTogglingAvailability(false);
}
};
const handlePhotoUpload = async (file: File) => { const handlePhotoUpload = async (file: File) => {
// Upload avatar to S3 (uses agent ID as filename for auto-replacement) // Upload avatar to S3 (uses agent ID as filename for auto-replacement)
const uploadedFile = await uploadService.uploadAvatar(file); const uploadedFile = await uploadService.uploadAvatar(file);
@@ -300,8 +325,12 @@ export default function AgentDashboard() {
</button> </button>
</div> </div>
{/* Status Buttons */} {/* Status Buttons - Toggle for agent to set availability */}
<StatusButtons /> <StatusButtons
isAvailable={isAvailable}
showToggle={true}
onToggleAvailability={handleToggleAvailability}
/>
{/* Contact Info */} {/* Contact Info */}
<ContactInfo <ContactInfo

View File

@@ -208,8 +208,8 @@ export default function AgentProfileView() {
</div> </div>
</div> </div>
{/* Status Buttons */} {/* Status Buttons - Public view shows availability status */}
<StatusButtons /> <StatusButtons isAvailable={agentProfile.isAvailable ?? true} />
{/* Contact Info */} {/* Contact Info */}
<ContactInfo <ContactInfo

View File

@@ -5,15 +5,23 @@ import { useRouter, usePathname } from 'next/navigation';
interface StatusButtonsProps { interface StatusButtonsProps {
isAvailable?: boolean; isAvailable?: boolean;
onToggleAvailability?: () => void;
showToggle?: boolean; // If true, show as toggle (for agent's own dashboard)
} }
export function StatusButtons({ isAvailable = true }: StatusButtonsProps) { export function StatusButtons({
isAvailable = true,
onToggleAvailability,
showToggle = false
}: StatusButtonsProps) {
const { data: session } = useSession(); const { data: session } = useSession();
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
// Handle connect click - require login if not authenticated // Handle connect click - require login if not authenticated
const handleConnectClick = () => { const handleConnectClick = () => {
if (!isAvailable) return; // Don't do anything if unavailable
if (!session) { if (!session) {
// Store the current page to redirect back after login // Store the current page to redirect back after login
const returnUrl = encodeURIComponent(pathname); const returnUrl = encodeURIComponent(pathname);
@@ -23,28 +31,73 @@ export function StatusButtons({ isAvailable = true }: StatusButtonsProps) {
// TODO: Handle connect action when logged in // TODO: Handle connect action when logged in
}; };
// If showToggle is true, render both options as a toggle selector (for agent dashboard)
if (showToggle) {
return ( return (
<div className="w-full max-w-[354px] lg:max-w-none space-y-2"> <div className="w-full max-w-[354px] lg:max-w-none space-y-2">
<div className="flex items-center border border-[#00293d]/10 rounded-[15px] h-[50px] px-4"> {/* Available Option */}
<button
onClick={() => !isAvailable && onToggleAvailability?.()}
className={`w-full flex items-center border rounded-[15px] h-[50px] px-4 transition-colors ${
isAvailable
? 'border-green-500 bg-green-50'
: 'border-[#00293d]/10 hover:border-[#00293d]/20'
}`}
>
<div className="flex items-center gap-2 flex-1"> <div className="flex items-center gap-2 flex-1">
<span className="w-3 h-3 bg-green-500 rounded-full" /> <span className={`w-3 h-3 rounded-full ${
isAvailable ? 'bg-green-500' : 'bg-white border border-gray-400'
}`} />
<span className="text-sm font-bold text-[#00293d] font-fractul">Available.</span> <span className="text-sm font-bold text-[#00293d] font-fractul">Available.</span>
</div> </div>
<button {isAvailable && (
onClick={handleConnectClick} <span className="text-xs text-green-600 font-medium">Active</span>
className="px-5 py-1.5 bg-[#e58625] text-white text-sm rounded-[15px] hover:bg-[#d47720] transition-colors font-fractul" )}
>
Connect
</button> </button>
</div>
<div className="flex items-center border border-[#00293d]/10 rounded-[15px] h-[50px] px-4"> {/* Unavailable Option */}
<button
onClick={() => isAvailable && onToggleAvailability?.()}
className={`w-full flex items-center border rounded-[15px] h-[50px] px-4 transition-colors ${
!isAvailable
? 'border-[#00293d] bg-[#00293d]/5'
: 'border-[#00293d]/10 hover:border-[#00293d]/20'
}`}
>
<div className="flex items-center gap-2 flex-1"> <div className="flex items-center gap-2 flex-1">
<span className="w-3 h-3 bg-white border border-gray-400 rounded-full" /> <span className={`w-3 h-3 rounded-full ${
!isAvailable ? 'bg-[#00293d]' : 'bg-white border border-gray-400'
}`} />
<span className="text-sm font-bold text-[#00293d] font-fractul">Unavailable.</span> <span className="text-sm font-bold text-[#00293d] font-fractul">Unavailable.</span>
</div> </div>
{!isAvailable && (
<span className="text-xs text-[#00293d]/70 font-medium">Active</span>
)}
</button>
</div>
);
}
// Public view - show single status with Connect button
return (
<div className="w-full max-w-[354px] lg:max-w-none">
<div className="flex items-center border border-[#00293d]/10 rounded-[15px] h-[50px] px-4">
<div className="flex items-center gap-2 flex-1">
<span className={`w-3 h-3 rounded-full ${
isAvailable ? 'bg-green-500' : 'bg-white border border-gray-400'
}`} />
<span className="text-sm font-bold text-[#00293d] font-fractul">
{isAvailable ? 'Available.' : 'Unavailable.'}
</span>
</div>
<button <button
onClick={handleConnectClick} onClick={handleConnectClick}
className="px-5 py-1.5 bg-[#00293d] text-white text-sm rounded-[15px] hover:bg-[#001d2b] transition-colors font-fractul" disabled={!isAvailable}
className={`px-5 py-1.5 text-white text-sm rounded-[15px] transition-colors font-fractul ${
isAvailable
? 'bg-[#e58625] hover:bg-[#d47720] cursor-pointer'
: 'bg-[#00293d] cursor-not-allowed opacity-70'
}`}
> >
Connect Connect
</button> </button>

View File

@@ -28,6 +28,7 @@ export interface AgentProfile {
isVerified: boolean; isVerified: boolean;
isFeatured: boolean; isFeatured: boolean;
isActive: boolean; isActive: boolean;
isAvailable: boolean;
agentTypeId: string | null; agentTypeId: string | null;
agentType: AgentType | null; agentType: AgentType | null;
user?: { user?: {
@@ -121,6 +122,7 @@ export interface PublicAgentProfile {
reviewCount: number; reviewCount: number;
isVerified: boolean; isVerified: boolean;
isFeatured: boolean; isFeatured: boolean;
isAvailable: boolean;
agentTypeId: string | null; agentTypeId: string | null;
agentType: AgentType | null; agentType: AgentType | null;
createdAt?: string; createdAt?: string;