import { useMemo, useState } from "react"; import { Link } from "react-router"; import { AlertCircle, ChevronDown, SearchX } from "lucide-react"; import { searchAccounts, fetchDistinctIndustries, fetchDistinctTypes, } from "../api/account/accountSearchService"; import { useCachedAsyncData } from "../features/object-search/hooks/useCachedAsyncData"; import { fieldValue } from "../features/object-search/utils/fieldUtils"; import { useObjectSearchParams } from "../features/object-search/hooks/useObjectSearchParams"; import { Alert, AlertTitle, AlertDescription } from "../components/ui/alert"; import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card"; import { Button } from "../components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "../components/ui/collapsible"; import { Skeleton } from "../components/ui/skeleton"; import { FilterProvider, FilterResetButton, } from "../features/object-search/components/FilterContext"; import { SearchFilter } from "../features/object-search/components/filters/SearchFilter"; import { TextFilter } from "../features/object-search/components/filters/TextFilter"; import { SelectFilter } from "../features/object-search/components/filters/SelectFilter"; import { MultiSelectFilter } from "../features/object-search/components/filters/MultiSelectFilter"; import { NumericRangeFilter } from "../features/object-search/components/filters/NumericRangeFilter"; import { DateFilter } from "../features/object-search/components/filters/DateFilter"; import { DateRangeFilter } from "../features/object-search/components/filters/DateRangeFilter"; import { ActiveFilters } from "../features/object-search/components/ActiveFilters"; import { SortControl } from "../features/object-search/components/SortControl"; import type { FilterFieldConfig } from "../features/object-search/utils/filterUtils"; import type { SortFieldConfig } from "../features/object-search/utils/sortUtils"; import type { Account_Filter, Account_OrderBy } from "../api/graphql-operations-types"; import type { AccountSearchResult } from "../api/account/accountSearchService"; import { ObjectBreadcrumb } from "../features/object-search/components/ObjectBreadcrumb"; import PaginationControls from "../features/object-search/components/PaginationControls"; import type { PaginationConfig } from "../features/object-search/hooks/useObjectSearchParams"; const PAGINATION_CONFIG: PaginationConfig = { defaultPageSize: 6, validPageSizes: [6, 12, 24, 48], }; type AccountNode = NonNullable< NonNullable[number]>["node"] >; const FILTER_CONFIGS: FilterFieldConfig[] = [ { field: "search", label: "Search", type: "search", searchFields: ["Name", "Phone", "Industry"], placeholder: "Search by name, phone, or industry...", }, { field: "Name", label: "Account Name", type: "text", placeholder: "Search by name..." }, { field: "Industry", label: "Industry", type: "picklist" }, { field: "Type", label: "Type", type: "multipicklist" }, { field: "AnnualRevenue", label: "Annual Revenue", type: "numeric" }, { field: "CreatedDate", label: "Created Date", type: "datetime" }, { field: "LastModifiedDate", label: "Last Modified Date", type: "datetimerange" }, ]; const ACCOUNT_SORT_CONFIGS: SortFieldConfig[] = [ { field: "Name", label: "Name" }, { field: "AnnualRevenue", label: "Annual Revenue" }, { field: "Industry", label: "Industry" }, { field: "CreatedDate", label: "Created Date" }, ]; // -- Component -------------------------------------------------------------- export default function AccountSearch() { const [filtersOpen, setFiltersOpen] = useState(true); const { data: industryOptions } = useCachedAsyncData(fetchDistinctIndustries, [], { key: "distinctIndustries", ttl: 300_000, }); const { data: typeOptions } = useCachedAsyncData(fetchDistinctTypes, [], { key: "distinctTypes", ttl: 300_000, }); const { filters, sort, query, pagination, resetAll } = useObjectSearchParams< Account_Filter, Account_OrderBy >(FILTER_CONFIGS, ACCOUNT_SORT_CONFIGS, PAGINATION_CONFIG); const searchKey = `accounts:${JSON.stringify({ where: query.where, orderBy: query.orderBy, first: pagination.pageSize, after: pagination.afterCursor })}`; const { data, loading, error } = useCachedAsyncData( () => searchAccounts({ where: query.where, orderBy: query.orderBy, first: pagination.pageSize, after: pagination.afterCursor, }), [query.where, query.orderBy, pagination.pageSize, pagination.afterCursor], { key: searchKey }, ); const pageInfo = data?.pageInfo; const totalCount = data?.totalCount; const hasNextPage = pageInfo?.hasNextPage ?? false; const hasPreviousPage = pagination.pageIndex > 0; const validAccountNodes = useMemo( () => (data?.edges ?? []).reduce((acc, edge) => { if (edge?.node) acc.push(edge.node); return acc; }, []), [data?.edges], ); return (

Search Accounts

{/* Sidebar — Filter Panel */} {/* Main area — Sort + Results */}
{/* Sort control + active filters */}
{/* Loading state */} {loading && ( <>
{Array.from({ length: pagination.pageSize }, (_, i) => (
))}
)} {/* Error state */} {error && ( <>

0 accounts found

Failed to load accounts Something went wrong while loading accounts. Please try again later. )} {/* Results list */} {!loading && !error && validAccountNodes.length > 0 && ( <>

{totalCount != null && (hasNextPage || hasPreviousPage) ? `${totalCount} account${totalCount !== 1 ? "s" : ""} found` : `Showing ${validAccountNodes.length} account${validAccountNodes.length !== 1 ? "s" : ""}`}

)} {/* No results state */} {!loading && !error && validAccountNodes.length === 0 && (

No accounts found

Try adjusting your filters or search criteria.

)}
{/* Pagination — always visible, disabled while loading or on error */} { if (pageInfo?.endCursor) pagination.goToNextPage(pageInfo.endCursor); }} onPreviousPage={pagination.goToPreviousPage} onPageSizeChange={pagination.setPageSize} disabled={loading || !!error} />
); } // -- Result Components ------------------------------------------------------ function AccountResultsList({ nodes }: { nodes: AccountNode[] }) { return (
    {nodes.map((node) => ( ))}
); } function AccountResultItem({ node }: { node: AccountNode }) { return (
  • {fieldValue(node.Name) ?? "\u2014"}

    {[fieldValue(node.Industry), fieldValue(node.Type)].filter(Boolean).join(" \u00B7 ") || "\u2014"}

    {fieldValue(node.Phone) ?? ""}

    {fieldValue(node.Owner?.Name) ?? ""}

  • ); }