feat: Introduce dynamic profile filtering with a new service and update user and agent profiles to use separate first and last name fields.

This commit is contained in:
pradeepkumar
2026-02-01 15:04:51 +05:30
parent 35ae58c0df
commit 09cf44f418
5 changed files with 167 additions and 333 deletions

View File

@@ -4,7 +4,8 @@ import { SettingsSidebar, ProfileSettingsForm } from '@/components/settings';
export default function ProfileSettingsPage() { export default function ProfileSettingsPage() {
const handleSave = (data: { const handleSave = (data: {
fullName: string; firstName: string;
lastName: string;
career: string; career: string;
email: string; email: string;
phone: string; phone: string;

View File

@@ -4,15 +4,16 @@ import { useState, useEffect, useCallback, Suspense } from 'react';
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { FilterModal, FilterState, clientSpecializations, loanTypes, propertyTypes, pricePoints } from '@/components/profiles/FilterModal'; import { FilterModal, FilterState, FilterField } from '@/components/profiles/FilterModal';
import { agentsService, AgentType, PublicAgentProfile, SearchAgentsParams } from '@/services/agents.service'; import { agentsService, AgentType, PublicAgentProfile, SearchAgentsParams } from '@/services/agents.service';
import { profileSectionsService, FilterableField } from '@/services/profile-sections.service';
import { uploadService } from '@/services/upload.service'; import { uploadService } from '@/services/upload.service';
interface FilterSectionProps { interface FilterSectionProps {
title: string; title: string;
options: string[]; options: { label: string; value: string }[];
selectedOptions: string[]; selectedOptions: string[];
onToggle: (option: string) => void; onToggle: (optionValue: string) => void;
showMore?: boolean; showMore?: boolean;
} }
@@ -32,14 +33,14 @@ function FilterSection({ title, options, selectedOptions, onToggle, showMore }:
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
{displayOptions.map((option) => ( {displayOptions.map((option) => (
<label key={option} className="flex items-center gap-2 cursor-pointer"> <label key={option.value} className="flex items-center gap-2 cursor-pointer">
<input <input
type="checkbox" type="checkbox"
checked={selectedOptions.includes(option)} checked={selectedOptions.includes(option.value)}
onChange={() => onToggle(option)} onChange={() => onToggle(option.value)}
className="w-4 h-4 rounded border-[#00293d]/30 text-[#e58625] focus:ring-[#e58625]" className="w-4 h-4 rounded border-[#00293d]/30 text-[#e58625] focus:ring-[#e58625]"
/> />
<span className="font-serif text-[13px] text-[#00293d]">{option}</span> <span className="font-serif text-[13px] text-[#00293d]">{option.label}</span>
</label> </label>
))} ))}
</div> </div>
@@ -239,48 +240,60 @@ function ProfilesPageContent() {
// State for API data // State for API data
const [agents, setAgents] = useState<PublicAgentProfile[]>([]); const [agents, setAgents] = useState<PublicAgentProfile[]>([]);
const [agentTypes, setAgentTypes] = useState<AgentType[]>([]); const [agentTypes, setAgentTypes] = useState<AgentType[]>([]);
const [filterFields, setFilterFields] = useState<FilterField[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [totalPages, setTotalPages] = useState(1); const [totalPages, setTotalPages] = useState(1);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [totalResults, setTotalResults] = useState(0); const [totalResults, setTotalResults] = useState(0);
// State for search and filters // State for search and filters - initialize from URL params
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState(() => searchParams.get('name') || '');
const [activeTypeId, setActiveTypeId] = useState<string | null>(null); const [activeTypeId, setActiveTypeId] = useState<string | null>(() => searchParams.get('type'));
const [isInitialized, setIsInitialized] = useState(false);
const [listingType, setListingType] = useState<'agents' | 'lenders'>('agents'); const [listingType, setListingType] = useState<'agents' | 'lenders'>('agents');
const [isFilterModalOpen, setIsFilterModalOpen] = useState(false); const [isFilterModalOpen, setIsFilterModalOpen] = useState(false);
const [filters, setFilters] = useState<FilterState>({ const [filters, setFilters] = useState<FilterState>({});
clientSpecialization: [],
loanType: [],
propertyType: [],
pricePoint: [],
specialInterests: [],
popularFilters: [],
});
// Initialize from URL params // Mark as initialized after first render and sync with URL param changes
useEffect(() => { useEffect(() => {
const type = searchParams.get('type'); setIsInitialized(true);
const name = searchParams.get('name'); }, []);
const location = searchParams.get('location');
if (type) setActiveTypeId(type); // Sync state when URL params change (e.g., browser back/forward)
if (name) setSearchQuery(name);
// Location is handled separately in search params
}, [searchParams]);
// Fetch agent types on mount
useEffect(() => { useEffect(() => {
const fetchAgentTypes = async () => { if (isInitialized) {
const type = searchParams.get('type');
const name = searchParams.get('name');
setActiveTypeId(type);
setSearchQuery(name || '');
}
}, [searchParams, isInitialized]);
// Fetch agent types and filter fields on mount
useEffect(() => {
const fetchInitialData = async () => {
try { try {
const types = await agentsService.getAgentTypes(); const [types, fields] = await Promise.all([
agentsService.getAgentTypes(),
profileSectionsService.getFilterableFields(),
]);
setAgentTypes(types); setAgentTypes(types);
// Convert FilterableField to FilterField format
const filterFieldsData: FilterField[] = fields.map((f: FilterableField) => ({
slug: f.slug,
name: f.name,
fieldType: f.fieldType,
options: f.options,
}));
setFilterFields(filterFieldsData);
} catch (error) { } catch (error) {
console.error('Failed to fetch agent types:', error); console.error('Failed to fetch initial data:', error);
} }
}; };
fetchAgentTypes(); fetchInitialData();
}, []); }, []);
// Fetch agents based on filters // Fetch agents based on filters
@@ -325,29 +338,25 @@ function ProfilesPageContent() {
} }
}, [currentPage, searchQuery, activeTypeId, searchParams]); }, [currentPage, searchQuery, activeTypeId, searchParams]);
// Fetch agents when filters change // Fetch agents when filters change (only after initialization)
useEffect(() => { useEffect(() => {
fetchAgents(); if (isInitialized) {
}, [fetchAgents]); fetchAgents();
}
}, [fetchAgents, isInitialized]);
const toggleFilter = (category: string, option: string) => { const toggleFilter = (fieldSlug: string, optionValue: string) => {
setFilters(prev => ({ setFilters(prev => {
...prev, const currentValues = prev[fieldSlug] || [];
[category]: (prev[category as keyof FilterState] || []).includes(option) const newValues = currentValues.includes(optionValue)
? (prev[category as keyof FilterState] || []).filter((o: string) => o !== option) ? currentValues.filter(v => v !== optionValue)
: [...(prev[category as keyof FilterState] || []), option] : [...currentValues, optionValue];
})); return { ...prev, [fieldSlug]: newValues };
});
}; };
const clearAllFilters = () => { const clearAllFilters = () => {
setFilters({ setFilters({});
clientSpecialization: [],
loanType: [],
propertyType: [],
pricePoint: [],
specialInterests: [],
popularFilters: [],
});
}; };
const applyFilters = () => { const applyFilters = () => {
@@ -366,6 +375,14 @@ function ProfilesPageContent() {
setCurrentPage(1); setCurrentPage(1);
}; };
// Get filter field by slug for sidebar
const getFilterFieldBySlug = (slug: string): FilterField | undefined => {
return filterFields.find(f => f.slug === slug);
};
// Define which fields to show in sidebar (main filter categories)
const sidebarFilterSlugs = ['client_specialization', 'loan_type', 'property_type', 'price_point'];
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
@@ -409,34 +426,27 @@ function ProfilesPageContent() {
</button> </button>
</div> </div>
<FilterSection {/* Dynamic Filter Sections */}
title="Client Specialization" {sidebarFilterSlugs.map(slug => {
options={clientSpecializations} const field = getFilterFieldBySlug(slug);
selectedOptions={filters.clientSpecialization} if (!field) return null;
onToggle={(option) => toggleFilter('clientSpecialization', option)} return (
/> <FilterSection
key={field.slug}
title={field.name}
options={field.options}
selectedOptions={filters[field.slug] || []}
onToggle={(optionValue) => toggleFilter(field.slug, optionValue)}
showMore={field.options.length > 6}
/>
);
})}
<FilterSection {filterFields.length === 0 && (
title="Loan Type" <div className="py-4">
options={loanTypes} <p className="font-serif text-[13px] text-[#00293d]/50">Loading filters...</p>
selectedOptions={filters.loanType} </div>
onToggle={(option) => toggleFilter('loanType', option)} )}
/>
<FilterSection
title="Property Type"
options={propertyTypes}
selectedOptions={filters.propertyType}
onToggle={(option) => toggleFilter('propertyType', option)}
showMore
/>
<FilterSection
title="Price Point"
options={pricePoints}
selectedOptions={filters.pricePoint}
onToggle={(option) => toggleFilter('pricePoint', option)}
/>
</div> </div>
</div> </div>
@@ -576,6 +586,7 @@ function ProfilesPageContent() {
isOpen={isFilterModalOpen} isOpen={isFilterModalOpen}
onClose={() => setIsFilterModalOpen(false)} onClose={() => setIsFilterModalOpen(false)}
filters={filters} filters={filters}
filterFields={filterFields}
onToggleFilter={toggleFilter} onToggleFilter={toggleFilter}
onClearAll={clearAllFilters} onClearAll={clearAllFilters}
onApply={applyFilters} onApply={applyFilters}

View File

@@ -4,7 +4,8 @@ import { SettingsSidebar, ProfileSettingsForm } from '@/components/settings';
export default function UserProfileSettingsPage() { export default function UserProfileSettingsPage() {
const handleSave = (data: { const handleSave = (data: {
fullName: string; firstName: string;
lastName: string;
career: string; career: string;
email: string; email: string;
phone: string; phone: string;

View File

@@ -2,61 +2,40 @@
import { useState } from 'react'; import { useState } from 'react';
// Filter options data // Types for dynamic filter data
export const clientSpecializations = [ export interface FilterOption {
'Buyers', 'Sellers', 'Divorce', 'First time buyers', 'First time sellers', 'Estate' label: string;
]; value: string;
export const loanTypes = [
'Conventional', 'FHA', 'VA', 'USDA', 'Downpayment Assistance Programs'
];
export const propertyTypes = [
'SFR', 'Townhome', 'condo', 'Manufactured', 'Apartment / Multifamily',
'Rural', 'Luxury', 'Retirement', 'Investment/ Income Producing',
'Duplex / Tri-plex / 4-plex', 'Historical', 'Mountain Property'
];
export const pricePoints = [
'Under $100K', '$100K - $300K', '$300K - $500K', '$500K - $1M', '$1M and above'
];
export const specialInterests = [
'Workshop / Trade / Craftspace', 'Gardening/Landscape', 'Boating',
'Automotive', 'Hobby farm', 'Home theater'
];
export const popularFilters = [
'Sellers Agent', 'First-Time Buyer', 'Buyers Agent', 'Buyers Agent',
'Buyers Agent', 'First-Time Seller', 'Buyers Agent'
];
export interface FilterState {
clientSpecialization: string[];
loanType: string[];
propertyType: string[];
pricePoint: string[];
specialInterests: string[];
popularFilters: string[];
} }
export interface FilterField {
slug: string;
name: string;
fieldType: string;
options: FilterOption[];
}
// Dynamic filter state - keys are field slugs
export type FilterState = Record<string, string[]>;
interface FilterModalProps { interface FilterModalProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
filters: FilterState; filters: FilterState;
onToggleFilter: (category: string, option: string) => void; filterFields: FilterField[];
onToggleFilter: (fieldSlug: string, optionValue: string) => void;
onClearAll: () => void; onClearAll: () => void;
onApply: () => void; onApply: () => void;
} }
export function FilterModal({ isOpen, onClose, filters, onToggleFilter, onClearAll, onApply }: FilterModalProps) { export function FilterModal({ isOpen, onClose, filters, filterFields, onToggleFilter, onClearAll, onApply }: FilterModalProps) {
const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({ const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>(() => {
popularFilters: true, // Initialize all sections as expanded
clientSpecialization: true, const initial: Record<string, boolean> = {};
loanType: true, filterFields.forEach(field => {
propertyType: true, initial[field.slug] = true;
specialInterests: true, });
pricePoint: true, return initial;
}); });
const toggleSection = (section: string) => { const toggleSection = (section: string) => {
@@ -84,217 +63,43 @@ export function FilterModal({ isOpen, onClose, filters, onToggleFilter, onClearA
{/* Scrollable Content */} {/* Scrollable Content */}
<div className="overflow-y-auto max-h-[calc(90vh-180px)] px-8 py-5"> <div className="overflow-y-auto max-h-[calc(90vh-180px)] px-8 py-5">
{/* Popular Filters */} {filterFields.map((field, index) => (
<div className="mb-6"> <div key={field.slug}>
<button <div className="mb-6">
onClick={() => toggleSection('popularFilters')} <button
className="flex items-center gap-2 font-fractul text-[20px] text-[#00293d] mb-4" onClick={() => toggleSection(field.slug)}
> className="flex items-center gap-2 font-fractul text-[20px] text-[#00293d] mb-4"
Popular Filters >
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className={`transition-transform ${expandedSections.popularFilters ? 'rotate-180' : ''}`}> {field.name}
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/> <svg width="12" height="12" viewBox="0 0 12 12" fill="none" className={`transition-transform ${expandedSections[field.slug] ? 'rotate-180' : ''}`}>
</svg> <path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</button> </svg>
{expandedSections.popularFilters && ( </button>
<div className="flex flex-wrap gap-2"> {expandedSections[field.slug] && (
{popularFilters.map((filter, index) => ( <div className={`grid ${field.options.length > 6 ? 'grid-cols-3' : 'grid-cols-4'} gap-x-4 gap-y-3`}>
<button {field.options.map((option) => (
key={index} <label key={option.value} className="flex items-center gap-2 cursor-pointer">
onClick={() => onToggleFilter('popularFilters', filter)} <input
className={`px-4 py-2.5 rounded-[15px] border border-[#00293d] font-serif text-[14px] transition-colors ${ type="checkbox"
filters.popularFilters?.includes(filter) checked={(filters[field.slug] || []).includes(option.value)}
? 'bg-[#00293d] text-white' onChange={() => onToggleFilter(field.slug, option.value)}
: 'bg-white text-[#00293d] hover:bg-[#00293d]/5' className="w-[17px] h-[17px] rounded-[5px] border-[#00293d]/20 text-[#e58625] focus:ring-[#e58625]"
}`} />
> <span className="font-serif text-[15px] text-[#00293d]">{option.label}</span>
{filter} </label>
</button> ))}
))} </div>
)}
</div> </div>
)} {index < filterFields.length - 1 && <div className="border-t border-[#d9d9d9] my-4" />}
</div> </div>
))}
<div className="border-t border-[#d9d9d9] my-4" /> {filterFields.length === 0 && (
<div className="text-center py-8">
{/* Client Specialization */} <p className="font-serif text-[15px] text-[#00293d]/60">Loading filters...</p>
<div className="mb-6"> </div>
<button )}
onClick={() => toggleSection('clientSpecialization')}
className="flex items-center gap-2 font-fractul text-[20px] text-[#00293d] mb-4"
>
Client Specialization
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className={`transition-transform ${expandedSections.clientSpecialization ? 'rotate-180' : ''}`}>
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
{expandedSections.clientSpecialization && (
<>
<div className="grid grid-cols-4 gap-x-4 gap-y-3">
{clientSpecializations.slice(0, 5).map((option) => (
<label key={option} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={filters.clientSpecialization.includes(option)}
onChange={() => onToggleFilter('clientSpecialization', option)}
className="w-[17px] h-[17px] rounded-[5px] border-[#00293d]/20 text-[#e58625] focus:ring-[#e58625]"
/>
<span className="font-serif text-[15px] text-[#00293d]">{option}</span>
</label>
))}
</div>
<button className="mt-3 font-serif text-[15px] text-[#00293d] font-light flex items-center gap-1">
Show More
<svg width="10" height="10" viewBox="0 0 12 12" fill="none">
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
</>
)}
</div>
<div className="border-t border-[#d9d9d9] my-4" />
{/* Loan Type */}
<div className="mb-6">
<button
onClick={() => toggleSection('loanType')}
className="flex items-center gap-2 font-fractul text-[20px] text-[#00293d] mb-4"
>
Loan Type
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className={`transition-transform ${expandedSections.loanType ? 'rotate-180' : ''}`}>
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
{expandedSections.loanType && (
<>
<div className="grid grid-cols-4 gap-x-4 gap-y-3">
{loanTypes.map((option) => (
<label key={option} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={filters.loanType.includes(option)}
onChange={() => onToggleFilter('loanType', option)}
className="w-[17px] h-[17px] rounded-[5px] border-[#00293d]/20 text-[#e58625] focus:ring-[#e58625]"
/>
<span className="font-serif text-[15px] text-[#00293d]">{option}</span>
</label>
))}
</div>
<button className="mt-3 font-serif text-[15px] text-[#00293d] font-light flex items-center gap-1">
Show More
<svg width="10" height="10" viewBox="0 0 12 12" fill="none">
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
</>
)}
</div>
<div className="border-t border-[#d9d9d9] my-4" />
{/* Property Type */}
<div className="mb-6">
<button
onClick={() => toggleSection('propertyType')}
className="flex items-center gap-2 font-fractul text-[20px] text-[#00293d] mb-4"
>
Property Type
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className={`transition-transform ${expandedSections.propertyType ? 'rotate-180' : ''}`}>
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
{expandedSections.propertyType && (
<>
<div className="grid grid-cols-3 gap-x-4 gap-y-3">
{propertyTypes.map((option) => (
<label key={option} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={filters.propertyType.includes(option)}
onChange={() => onToggleFilter('propertyType', option)}
className="w-[17px] h-[17px] rounded-[5px] border-[#00293d]/20 text-[#e58625] focus:ring-[#e58625]"
/>
<span className="font-serif text-[15px] text-[#00293d]">{option}</span>
</label>
))}
</div>
<button className="mt-3 font-serif text-[15px] text-[#00293d] font-light flex items-center gap-1">
Show More
<svg width="10" height="10" viewBox="0 0 12 12" fill="none">
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
</>
)}
</div>
<div className="border-t border-[#d9d9d9] my-4" />
{/* Special Interests and Hobbies */}
<div className="mb-6">
<button
onClick={() => toggleSection('specialInterests')}
className="flex items-center gap-2 font-fractul text-[20px] text-[#00293d] mb-4"
>
Special Interests and Hobbies
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className={`transition-transform ${expandedSections.specialInterests ? 'rotate-180' : ''}`}>
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
{expandedSections.specialInterests && (
<>
<div className="grid grid-cols-3 gap-x-4 gap-y-3">
{specialInterests.map((option) => (
<label key={option} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={filters.specialInterests?.includes(option) || false}
onChange={() => onToggleFilter('specialInterests', option)}
className="w-[17px] h-[17px] rounded-[5px] border-[#00293d]/20 text-[#e58625] focus:ring-[#e58625]"
/>
<span className="font-serif text-[15px] text-[#00293d]">{option}</span>
</label>
))}
</div>
<button className="mt-3 font-serif text-[15px] text-[#00293d] font-light flex items-center gap-1">
Show More
<svg width="10" height="10" viewBox="0 0 12 12" fill="none">
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
</>
)}
</div>
<div className="border-t border-[#d9d9d9] my-4" />
{/* Price Point */}
<div className="mb-6">
<button
onClick={() => toggleSection('pricePoint')}
className="flex items-center gap-2 font-fractul text-[20px] text-[#00293d] mb-4"
>
Price Point
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" className={`transition-transform ${expandedSections.pricePoint ? 'rotate-180' : ''}`}>
<path d="M2 4L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</button>
{expandedSections.pricePoint && (
<div className="grid grid-cols-3 gap-x-4 gap-y-3">
{pricePoints.map((option) => (
<label key={option} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={filters.pricePoint.includes(option)}
onChange={() => onToggleFilter('pricePoint', option)}
className="w-[17px] h-[17px] rounded-[5px] border-[#00293d]/20 text-[#e58625] focus:ring-[#e58625]"
/>
<span className="font-serif text-[15px] text-[#00293d]">{option}</span>
</label>
))}
</div>
)}
</div>
</div> </div>
{/* Footer */} {/* Footer */}

View File

@@ -97,6 +97,14 @@ interface ApiResponse<T> {
message?: string; message?: string;
} }
// Filterable field structure from backend
export interface FilterableField {
slug: string;
name: string;
fieldType: string;
options: FieldOption[];
}
// Profile Sections Service for Web // Profile Sections Service for Web
class ProfileSectionsService { class ProfileSectionsService {
private basePath = '/profile-sections'; private basePath = '/profile-sections';
@@ -116,6 +124,14 @@ class ProfileSectionsService {
const response = await api.get<ApiResponse<ProfileSection[]>>(url); const response = await api.get<ApiResponse<ProfileSection[]>>(url);
return response.data.data; return response.data.data;
} }
/**
* Get all filterable fields with options (for search filters)
*/
async getFilterableFields(): Promise<FilterableField[]> {
const response = await api.get<ApiResponse<FilterableField[]>>('/profile-fields/filterable');
return response.data.data;
}
} }
export const profileSectionsService = new ProfileSectionsService(); export const profileSectionsService = new ProfileSectionsService();