Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dd6cde2
useResultsTableSearch wip
jagu65 Mar 30, 2026
d4d4175
search mostly working in console
jagu65 Mar 31, 2026
bfeda19
state management wip
jagu65 Mar 31, 2026
54a2afe
useResultsTableSearch implementation wip
jagu65 Mar 31, 2026
0d29d5c
tab counts wip
jagu65 Apr 1, 2026
07f2a03
subawards typo and phasing out connect
jagu65 Apr 1, 2026
26b5962
removed connect()
jagu65 Apr 1, 2026
3a333be
Merge branch 'refs/heads/qat' into mod/DEV-14599
jagu65 Apr 1, 2026
dd91ada
npm i
jagu65 Apr 1, 2026
463b7f1
Merge remote-tracking branch 'origin/qat' into mod/DEV-14599
jagu65 Apr 14, 2026
274cb3d
memoized dsmContent
jagu65 Apr 14, 2026
4a26413
memoized, no selector destructure
jagu65 Apr 14, 2026
d53440a
removed unused state
jagu65 Apr 14, 2026
9fe4dcf
Merge remote-tracking branch 'origin/qat' into mod/DEV-14599
jagu65 Apr 16, 2026
789b32c
Merge remote-tracking branch 'origin/qat' into mod/DEV-14599
jagu65 Apr 20, 2026
20f1013
bad network calls fixed
jagu65 Apr 20, 2026
a585276
switch to useMutation
jagu65 Apr 22, 2026
8290794
Merge remote-tracking branch 'origin/qat' into mod/DEV-14599
jagu65 May 12, 2026
49bde81
finally got it to not double render on search
jagu65 May 13, 2026
8d5f225
finally got it to not make bad calls
jagu65 May 14, 2026
f364ac9
I now actually added the promises properly
jagu65 May 14, 2026
c2cef6a
Merge remote-tracking branch 'origin/qat' into mod/DEV-14599
jagu65 May 14, 2026
66ab94c
minor refactor
jagu65 May 14, 2026
784de92
minor refactor
jagu65 May 14, 2026
eb91ec5
Added tanstack query to tab counts
jagu65 May 14, 2026
e207708
moved query to custom hook
jagu65 May 14, 2026
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
140 changes: 35 additions & 105 deletions src/js/components/search/resultsView/ResultsView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@
* Created by Andrea Blackwell
**/

import React, { useEffect, useState } from "react";
import React from "react";
import PropTypes from "prop-types";
import { isCancel } from "axios";
import { useSelector } from "react-redux";

import TopFilterBarContainer from "containers/search/topFilterBar/TopFilterBarContainer";
import SearchAwardsOperation from "models/v1/search/SearchAwardsOperation";
import { performSpendingByAwardTabCountSearch, areFiltersEqual } from "helpers/searchHelper";
import { performTabCountSearch } from "helpers/keywordHelper";
import useResultsCount from "containers/search/resultsView/useResultsCount";
import NewSearchScreen from "./NewSearchScreen";
import NoDataScreen from "./NoDataScreen";
import SectionsContent from "./SectionsContent";
Expand All @@ -34,124 +31,57 @@ const ResultsView = React.memo(function ResultsView({
hash,
setFilterCount
}) {
const [hasResults, setHasResults] = useState(false);
const [resultContent, setResultContent] = useState(null);
const [tabData, setTabData] = useState();
const [inFlight, setInFlight] = useState(false);
const [error, setError] = useState(false);

const filters = useSelector((state) => state.appliedFilters.filters);
const spendingLevel = useSelector((state) => state.searchView.spendingLevel);

let countRequest;

const checkForData = () => {
if (countRequest) {
countRequest.cancel();
}

const searchParamsTemp = new SearchAwardsOperation();
searchParamsTemp.fromState(filters);
const { data, error } = useResultsCount(filters, spendingLevel, hash);

setInFlight(true);
setError(false);
let content = null;

if (spendingLevel === 'transactions') {
countRequest = performTabCountSearch({
filters: searchParamsTemp.toParams(),
spending_level: spendingLevel,
auditTrail: 'Results View - Tab Counts'
});
}
else {
// if subawards is true, newAwardsOnly cannot be true, so we remove dateType
if (spendingLevel === 'subawards') {
delete searchParamsTemp.dateType;
}
if (!error && data) {
/* eslint-disable camelcase */
const {
contracts, direct_payments, grants, idvs, loans, other, subgrants, subcontracts
} = data.data.results;
let resCount = contracts + direct_payments + grants + idvs + loans + other;

countRequest = performSpendingByAwardTabCountSearch({
filters: searchParamsTemp.toParams(),
spending_level: spendingLevel,
auditTrail: 'Results View - Tab Counts'
});
if (spendingLevel === 'subawards') {
resCount = subgrants + subcontracts;
}

countRequest.promise
.then((res) => {
/* eslint-disable camelcase */
setTabData(res.data);
const {
contracts, direct_payments, grants, idvs, loans, other, subgrants, subcontracts
} = res.data.results;
let resCount = contracts + direct_payments + grants + idvs + loans + other;
const hasResults = resCount > 0;
/* eslint-enable camelcase */

if (spendingLevel === 'subawards') {
resCount = subgrants + subcontracts;
}
/* eslint-enable camelcase */

if (resCount > 0) {
setHasResults(true);
}
else {
setHasResults(false);
}

setInFlight(false);
setError(false);
})
.catch((err) => {
if (!isCancel(err)) {
setInFlight(false);
setError(true);
console.log(err);
}
});
};

useEffect(() => {
if (!areFiltersEqual(filters) || !hash) {
checkForData();
if (!hash && noFiltersApplied) {
content = <NewSearchScreen />;
}

return () => {
countRequest?.cancel();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters, spendingLevel]);
useEffect(() => {
let content = null;

if (!inFlight && !error) {
if (!hash && noFiltersApplied) {
content = <NewSearchScreen />;
if (!noFiltersApplied) {
if (hasResults) {
content = (
<SectionsContent
tabData={data.data}
hash={hash}
spendingLevel={spendingLevel} />
);
}

if (!noFiltersApplied) {
if (hasResults) {
content = (
<SectionsContent
tabData={tabData}
hash={hash}
spendingLevel={spendingLevel} />
);
}
else {
content = <NoDataScreen />;
}
else {
content = <NoDataScreen />;
}
}

setResultContent(content);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [noFiltersApplied, hasResults, inFlight, error, hash]);
}

return (
<div className="search-results-view-container">
<div className="search-results-wrapper">
<TopFilterBarContainer resultsView filters={filters} setFilterCount={setFilterCount} />
<div className={`search-results ${showMobileFilters && isMobile ? 'behind-filters' : ''}`}>
{resultContent}
<TopFilterBarContainer
resultsView
filters={filters}
setFilterCount={setFilterCount} />
<div className={`search-results ${
showMobileFilters && isMobile ? 'behind-filters' : ''
}`}>
{content}
</div>
</div>
</div>
Expand Down
57 changes: 30 additions & 27 deletions src/js/components/search/resultsView/SectionsContent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,34 @@
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";

import Analytics from "helpers/analytics/Analytics";
import TableSection from "./table/TableSection";
import CategoriesSection from "./categories/CategoriesSection";
import TimeSection from "./time/TimeSection";
import MapSection from "./map/MapSection";
import Analytics from "../../../helpers/analytics/Analytics";

require("pages/search/searchPage.scss");

const logVisualizationViewEvent = (action, label) => window.setTimeout(
() => Analytics.event({
event: 'search_visualization_type',
category: 'Advanced Search - Visualization Type',
action,
gtm: true,
label
}), 15 * 1000);

const propTypes = {
tabData: PropTypes.object,
hash: PropTypes.string,
spendingLevel: PropTypes.string
};

const SectionsContent = (props) => {
const SectionsContent = ({
tabData,
hash,
spendingLevel
}) => {
const [observerSupported, setObserverSupported] = useState(false);
const [timeHasLoaded, setTimeHasLoaded] = useState(false);
const [categoriesHasLoaded, setCategoriesHasLoaded] = useState(false);
Expand All @@ -30,44 +43,31 @@ const SectionsContent = (props) => {
threshold: 0.1
};

const logVisualizationViewEvent = (activeLabel) => {
window.setTimeout(() => {
Analytics.event({
event: 'search_visualization_type',
category: 'Advanced Search - Visualization Type',
action: activeLabel,
gtm: true,
label: props.hash
});
}, 15 * 1000);
};

const callbackFunction = (entries) => {
entries.forEach((entry) => {
const section = entry.target.className;
if (entry.isIntersecting) {
if (section === 'awards') {
logVisualizationViewEvent("awards");
logVisualizationViewEvent("awards", hash);
}
else if (section === 'time') {
setTimeHasLoaded(true);
logVisualizationViewEvent("time");
logVisualizationViewEvent("time", hash);
}
else if (section === 'categories') {
setCategoriesHasLoaded(true);
logVisualizationViewEvent("categories");
logVisualizationViewEvent("categories", hash);
}
else if (section === "map") {
setMapHasLoaded(true);
logVisualizationViewEvent("map");
logVisualizationViewEvent("map", hash);
}
}
});
};

useEffect(() => {
setObserverSupported('IntersectionObserver' in window);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// eslint-disable-next-line consistent-return
Expand All @@ -88,25 +88,28 @@ const SectionsContent = (props) => {
return () => observer.disconnect();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [observerSupported, props.hash]);
}, [observerSupported, hash]);

return (
<>
<TableSection {...props} />
<TableSection
tabData={tabData}
hash={hash}
spendingLevel={spendingLevel} />
<CategoriesSection
spendingLevel={props.spendingLevel}
spendingLevel={spendingLevel}
categoriesHasLoaded={categoriesHasLoaded}
setSelectedDropdown={setSelectedDropdown}
selectedDropdown={selectedDropdown}
hash={props.hash} />
hash={hash} />
<TimeSection
timeHasLoaded={timeHasLoaded}
hash={props.hash}
spendingLevel={props.spendingLevel} />
hash={hash}
spendingLevel={spendingLevel} />
<MapSection
spendingLevel={props.spendingLevel}
spendingLevel={spendingLevel}
mapHasLoaded={mapHasLoaded}
hash={props.hash} />
hash={hash} />
</>
);
};
Expand Down
24 changes: 8 additions & 16 deletions src/js/components/search/resultsView/table/TableSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
* TableSection.jsx
*/

import React from "react";
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import ResultsTableContainer from "../../../../containers/search/resultsView/ResultsTableContainer";
import ResultsTableContainer from "containers/search/resultsView/ResultsTableContainer";

import TableDsm from "./TableDsm";

Expand All @@ -17,27 +17,19 @@ const propTypes = {
const TableSection = ({
tabData, hash, spendingLevel
}) => {
const sectionTitle = () => {
switch (spendingLevel) {
case 'awards': return 'Prime Award Results';
case 'subawards': return 'Subaward Results';
default: return 'Transaction Results';
}
};
const sectionTitle = spendingLevel === "awards" ? 'Prime Award Results' : 'Subaward Results';

const wrapperProps = {
sectionTitle: sectionTitle(),
dsmContent: <TableDsm spendingLevel={spendingLevel} />,
sectionName: 'table'
};
const dsmContent = useMemo(() => <TableDsm spendingLevel={spendingLevel} />, [spendingLevel]);

return (
<div id="search-page-component" className="awards">
<ResultsTableContainer
tabData={tabData}
wrapperProps={wrapperProps}
hash={hash}
spendingLevel={spendingLevel} />
spendingLevel={spendingLevel}
sectionTitle={sectionTitle}
dsmContent={dsmContent}
sectionName="table" />
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import * as SearchHelper from 'helpers/searchHelper';
import { defaultColumns, defaultSort } from 'dataMapping/search/awardTableColumns';
import AccountAwardSearchOperation from 'models/v1/account/queries/AccountAwardSearchOperation';
import ResultsTableSection from 'components/search/resultsView/table/ResultsTableSection';
import { tableTypes, subTypes } from 'containers/search/resultsView/ResultsTableContainer';
import { tableTypes, subTypes } from 'dataMapping/search/resultsView/table';
import { SectionHeader } from "data-transparency-ui";

const propTypes = {
Expand Down
Loading
Loading