@abcagency/hc-ui-components 1.5.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/HireControlMap.js +52 -22
- package/dist/components/HireControlMap.js.map +1 -1
- package/dist/components/containers/accordions/filter-container.js +13 -5
- package/dist/components/containers/accordions/filter-container.js.map +1 -1
- package/dist/components/containers/accordions/filter-item-container.js +9 -9
- package/dist/components/containers/accordions/filter-item-container.js.map +1 -1
- package/dist/components/containers/accordions/map-accordion-item-container.js +10 -5
- package/dist/components/containers/accordions/map-accordion-item-container.js.map +1 -1
- package/dist/components/containers/filter/commute-container.js +3 -3
- package/dist/components/containers/filter/commute-container.js.map +1 -1
- package/dist/components/containers/filter/filter-container.js +6 -5
- package/dist/components/containers/filter/filter-container.js.map +1 -1
- package/dist/components/containers/filter/filter-item-container.js +12 -12
- package/dist/components/containers/filter/filter-item-container.js.map +1 -1
- package/dist/components/containers/filter/location-container.js +6 -5
- package/dist/components/containers/filter/location-container.js.map +1 -1
- package/dist/components/containers/filter/points-of-interest-container.js +5 -5
- package/dist/components/containers/filter/points-of-interest-container.js.map +1 -1
- package/dist/components/containers/filter/points-of-interest-radio-item-container.js +2 -2
- package/dist/components/containers/filter/points-of-interest-radio-item-container.js.map +1 -1
- package/dist/components/containers/filter/search-container.js +2 -2
- package/dist/components/containers/filter/search-container.js.map +1 -1
- package/dist/components/containers/jobListing/listing-details-container.js +2 -2
- package/dist/components/containers/jobListing/listing-details-container.js.map +1 -1
- package/dist/components/containers/list/item-list-container.js +8 -8
- package/dist/components/containers/list/item-list-container.js.map +1 -1
- package/dist/components/containers/list/list-item/list-item-container.js +2 -2
- package/dist/components/containers/list/list-item/list-item-container.js.map +1 -1
- package/dist/components/containers/maps/info-window-content-container.js +2 -2
- package/dist/components/containers/maps/info-window-content-container.js.map +1 -1
- package/dist/components/containers/maps/map-container.js +8 -9
- package/dist/components/containers/maps/map-container.js.map +1 -1
- package/dist/components/containers/maps/map-list-container.js +5 -5
- package/dist/components/containers/maps/map-list-container.js.map +1 -1
- package/dist/components/containers/maps/map-marker-container.js +6 -6
- package/dist/components/containers/maps/map-marker-container.js.map +1 -1
- package/dist/components/modules/accordions/MapAccordionItem.js +5 -5
- package/dist/components/modules/accordions/MapAccordionItem.js.map +1 -1
- package/dist/components/modules/accordions/default.js +15 -15
- package/dist/components/modules/accordions/default.js.map +1 -1
- package/dist/components/modules/accordions/filterItem.js +4 -4
- package/dist/components/modules/accordions/filterItem.js.map +1 -1
- package/dist/components/modules/accordions/filters.js +6 -6
- package/dist/components/modules/accordions/filters.js.map +1 -1
- package/dist/components/modules/buttons/button-group-apply.js +19 -19
- package/dist/components/modules/buttons/button-group-apply.js.map +1 -1
- package/dist/components/modules/buttons/default.js +9 -9
- package/dist/components/modules/buttons/default.js.map +1 -1
- package/dist/components/modules/buttons/items-pill.js +2 -2
- package/dist/components/modules/buttons/items-pill.js.map +1 -1
- package/dist/components/modules/buttons/pill-wrapper.js +2 -2
- package/dist/components/modules/buttons/pill-wrapper.js.map +1 -1
- package/dist/components/modules/buttons/show-all-button.js +3 -3
- package/dist/components/modules/buttons/show-all-button.js.map +1 -1
- package/dist/components/modules/cards/default.js +11 -11
- package/dist/components/modules/cards/default.js.map +1 -1
- package/dist/components/modules/cards/filter.js +5 -5
- package/dist/components/modules/cards/filter.js.map +1 -1
- package/dist/components/modules/dialogs/apply-dialog.js +19 -19
- package/dist/components/modules/dialogs/apply-dialog.js.map +1 -1
- package/dist/components/modules/filter/commute.js +18 -19
- package/dist/components/modules/filter/commute.js.map +1 -1
- package/dist/components/modules/filter/index.js +10 -10
- package/dist/components/modules/filter/index.js.map +1 -1
- package/dist/components/modules/filter/item.js +6 -6
- package/dist/components/modules/filter/item.js.map +1 -1
- package/dist/components/modules/filter/location.js +6 -6
- package/dist/components/modules/filter/location.js.map +1 -1
- package/dist/components/modules/filter/radio-item.js +6 -6
- package/dist/components/modules/filter/radio-item.js.map +1 -1
- package/dist/components/modules/filter/search.js +10 -10
- package/dist/components/modules/filter/search.js.map +1 -1
- package/dist/components/modules/filter/sort.js +28 -28
- package/dist/components/modules/filter/sort.js.map +1 -1
- package/dist/components/modules/grid.js +4 -4
- package/dist/components/modules/grid.js.map +1 -1
- package/dist/components/modules/icon.js +4 -4
- package/dist/components/modules/icon.js.map +1 -1
- package/dist/components/modules/jobListing/listing-details.js +4 -4
- package/dist/components/modules/jobListing/listing-details.js.map +1 -1
- package/dist/components/modules/list/field-mapper-desktop.js +8 -8
- package/dist/components/modules/list/field-mapper-desktop.js.map +1 -1
- package/dist/components/modules/list/field-mapper-mobile.js +18 -18
- package/dist/components/modules/list/field-mapper-mobile.js.map +1 -1
- package/dist/components/modules/list/header-item.js +7 -7
- package/dist/components/modules/list/header-item.js.map +1 -1
- package/dist/components/modules/list/header.js +5 -5
- package/dist/components/modules/list/header.js.map +1 -1
- package/dist/components/modules/list/item-expand-card/index.js +3 -3
- package/dist/components/modules/list/item-expand-card/index.js.map +1 -1
- package/dist/components/modules/list/item-list.js +15 -15
- package/dist/components/modules/list/item-list.js.map +1 -1
- package/dist/components/modules/list/list-item/list-item.js +13 -13
- package/dist/components/modules/list/list-item/list-item.js.map +1 -1
- package/dist/components/modules/maps/info-window-card.js +2 -2
- package/dist/components/modules/maps/info-window-card.js.map +1 -1
- package/dist/components/modules/maps/info-window-content.js +5 -5
- package/dist/components/modules/maps/info-window-content.js.map +1 -1
- package/dist/components/modules/maps/map-list.js +5 -5
- package/dist/components/modules/maps/map-list.js.map +1 -1
- package/dist/components/modules/maps/map-marker.js +3 -3
- package/dist/components/modules/maps/map-marker.js.map +1 -1
- package/dist/components/modules/maps/map.js +5 -5
- package/dist/components/modules/maps/map.js.map +1 -1
- package/dist/components/modules/maps/place-marker.js +5 -5
- package/dist/components/modules/maps/place-marker.js.map +1 -1
- package/dist/components/modules/maps/tabs.js +21 -21
- package/dist/components/modules/maps/tabs.js.map +1 -1
- package/dist/contexts/mapContext.js +18 -18
- package/dist/contexts/mapContext.js.map +1 -1
- package/dist/contexts/mapListContext.js +28 -24
- package/dist/contexts/mapListContext.js.map +1 -1
- package/dist/contexts/placesContext.js +2 -2
- package/dist/contexts/placesContext.js.map +1 -1
- package/dist/contexts/themeContext.js +2 -2
- package/dist/contexts/themeContext.js.map +1 -1
- package/dist/contexts/trackEventContext.js +2 -2
- package/dist/contexts/trackEventContext.js.map +1 -1
- package/dist/services/listingAggregatorService.js +19 -15
- package/dist/services/listingAggregatorService.js.map +1 -1
- package/dist/services/listingEntityService.js +3 -2
- package/dist/services/listingEntityService.js.map +1 -1
- package/dist/services/listingService.js +1 -16
- package/dist/services/listingService.js.map +1 -1
- package/dist/styles/index.css +1 -3
- package/dist/types/contexts/mapContext.d.ts +1 -0
- package/dist/types/services/listingAggregatorService.d.ts +2 -2
- package/dist/types/services/listingEntityService.d.ts +2 -3
- package/dist/types/types/GetListingParams.d.ts +1 -1
- package/dist/types/types/ListingEntity.d.ts +2 -1
- package/dist/types/types/ListingFields.d.ts +4 -2
- package/dist/types/types/Listings.d.ts +0 -1
- package/dist/types/util/algoliaSearchUtil.d.ts +4 -0
- package/dist/types/util/filterUtil.d.ts +2 -2
- package/dist/types/util/mapUtil.d.ts +3 -3
- package/dist/util/algoliaSearchUtil.js +133 -0
- package/dist/util/algoliaSearchUtil.js.map +1 -0
- package/dist/util/filterUtil.js +129 -66
- package/dist/util/filterUtil.js.map +1 -1
- package/dist/util/loading.js +3 -3
- package/dist/util/loading.js.map +1 -1
- package/dist/util/mapUtil.js +37 -25
- package/dist/util/mapUtil.js.map +1 -1
- package/package.json +60 -17
- package/src/components/HireControlMap.js +35 -9
- package/src/components/containers/accordions/filter-container.js +6 -1
- package/src/components/containers/accordions/filter-item-container.js +2 -2
- package/src/components/containers/accordions/map-accordion-item-container.js +6 -2
- package/src/components/containers/filter/filter-container.js +3 -2
- package/src/components/containers/filter/filter-item-container.js +10 -10
- package/src/components/containers/filter/location-container.js +3 -3
- package/src/components/containers/list/item-list-container.tsx +3 -3
- package/src/components/containers/maps/info-window-content-container.js +1 -1
- package/src/components/containers/maps/map-container.js +2 -1
- package/src/components/modules/buttons/button-group-apply.js +8 -8
- package/src/components/modules/dialogs/apply-dialog.js +2 -2
- package/src/components/modules/list/field-mapper-desktop.jsx +2 -2
- package/src/components/modules/list/field-mapper-mobile.jsx +8 -8
- package/src/components/modules/list/header-item.js +1 -1
- package/src/components/modules/maps/map-list.js +1 -1
- package/src/contexts/mapContext.tsx +17 -16
- package/src/contexts/mapListContext.tsx +55 -49
- package/src/services/listingAggregatorService.ts +29 -21
- package/src/services/listingEntityService.ts +3 -3
- package/src/services/listingService.ts +1 -11
- package/src/styles/components.css +30 -0
- package/src/types/GetListingParams.ts +1 -1
- package/src/types/ListingEntity.ts +2 -1
- package/src/types/ListingFields.ts +4 -2
- package/src/types/Listings.ts +0 -1
- package/src/util/algoliaSearchUtil.js +91 -0
- package/src/util/filterUtil.js +19 -8
- package/src/util/mapUtil.js +52 -41
|
@@ -53,6 +53,7 @@ interface MapListContextProps {
|
|
|
53
53
|
hiddenFilters?: string[];
|
|
54
54
|
containerStyle?: any;
|
|
55
55
|
ExpandListComponent?: React.ComponentType<{ listing: any }> | ((listing: any) => JSX.Element) | null;
|
|
56
|
+
noEntities?: boolean;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
const MapListContext = createContext<MapListContextProps | undefined>(undefined);
|
|
@@ -93,8 +94,11 @@ interface MapListProviderProps {
|
|
|
93
94
|
defaultFilters?: Record<string, any>;
|
|
94
95
|
containerStyle?: any;
|
|
95
96
|
localStorageKey: string;
|
|
96
|
-
getListingEntitiesCallback?: (
|
|
97
|
+
getListingEntitiesCallback?: (origin?: string) => Promise<any>;
|
|
97
98
|
ExpandListComponent?: React.ComponentType<{ listing: Listing }> | ((listing: Listing) => JSX.Element) | null;
|
|
99
|
+
hideMap?: boolean;
|
|
100
|
+
hideFilters?: boolean;
|
|
101
|
+
noEntities?: boolean;
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
@@ -118,13 +122,16 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
|
118
122
|
containerStyle,
|
|
119
123
|
ExpandListComponent,
|
|
120
124
|
getListingEntitiesCallback,
|
|
121
|
-
localStorageKey
|
|
125
|
+
localStorageKey,
|
|
126
|
+
hideMap = false,
|
|
127
|
+
hideFilters = false,
|
|
128
|
+
noEntities = false
|
|
122
129
|
}) => {
|
|
123
130
|
const firstLoadFilters = () =>{
|
|
124
131
|
let urlFilters = filtersFromURL(window.location)?.filters;
|
|
125
132
|
return (setFiltersUrl === true && urlFilters && Object.keys(urlFilters).length > 0) ? urlFilters : getStorageObject(localStorageKey + 'selectedFilters', {}) || {}
|
|
126
133
|
}
|
|
127
|
-
|
|
134
|
+
|
|
128
135
|
const firstLoadQuery = (): string | null => {
|
|
129
136
|
if (resetFilters) return null;
|
|
130
137
|
// Check URL first
|
|
@@ -135,7 +142,7 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
|
135
142
|
// Fall back to localStorage
|
|
136
143
|
return getQuery(localStorageKey);
|
|
137
144
|
}
|
|
138
|
-
|
|
145
|
+
|
|
139
146
|
const [allListings, setAllListings] = useState<Listing[]>([]);
|
|
140
147
|
const [filteredListings, setFilteredListings] = useState<Listing[]>([]);
|
|
141
148
|
const [loading, setLoading] = useState<boolean>(false);
|
|
@@ -144,7 +151,7 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
|
144
151
|
const [sortSetting, setSortSetting] = useState<{ field: string; type: string }>(getStorageObject(localStorageKey + 'sortSetting', { field: 'position', type: 'asc' }) || { field: 'position', type: 'asc' });
|
|
145
152
|
const [listingEntities, setListingEntities] = useState<Record<number, ListingEntity> | null>({});
|
|
146
153
|
const [firstLoad, setFirstLoad] = useState<boolean>(true);
|
|
147
|
-
const [commuteLocation, setCommuteLocation] = useState<any | null>(getStorageObject('commuteLocation'));
|
|
154
|
+
const [commuteLocation, setCommuteLocation] = useState<any | null>(getStorageObject(localStorageKey + 'commuteLocation'));
|
|
148
155
|
const [selectedFilters, setSelectedFilters] = useState<Record<string, any>>(() => resetFilters ? {} : firstLoadFilters());
|
|
149
156
|
const [filterOptions, setFilterOptions] = useState<any>();
|
|
150
157
|
const [recruiters, setRecruiters] = useState<Record<number, Recruiter>>({});
|
|
@@ -161,7 +168,7 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
|
161
168
|
if (!sortSetting) return;
|
|
162
169
|
localStorage.setItem(localStorageKey + 'sortSetting', JSON.stringify(sortSetting));
|
|
163
170
|
setNewFilteredListings(filteredListings);
|
|
164
|
-
}, [sortSetting, localStorageKey]);
|
|
171
|
+
}, [sortSetting, localStorageKey, filteredListings]);
|
|
165
172
|
|
|
166
173
|
useEffect(() => {
|
|
167
174
|
const loadedFavorites = JSON.parse(localStorage.getItem(localStorageKey + 'favorites') || '[]');
|
|
@@ -172,34 +179,32 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
|
172
179
|
setStorageObject(localStorageKey + "commuteLocation", commuteLocation);
|
|
173
180
|
}, [commuteLocation, localStorageKey]);
|
|
174
181
|
|
|
182
|
+
|
|
175
183
|
useEffect(() => {
|
|
176
|
-
if (!commuteLocation) return;
|
|
184
|
+
if (!commuteLocation || noEntities) return;
|
|
177
185
|
|
|
178
186
|
async function fetchEntities() {
|
|
179
|
-
const distinctEntityIds = [
|
|
180
|
-
...new Set(allListings.map(listing => listing.entityId ?? -1))
|
|
181
|
-
];
|
|
182
187
|
try {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
);
|
|
188
|
+
let fetchedEntities;
|
|
189
|
+
if (getListingEntitiesCallback) {
|
|
190
|
+
fetchedEntities = await getListingEntitiesCallback(`${commuteLocation.lat}, ${commuteLocation.lng}`);
|
|
191
|
+
} else {
|
|
192
|
+
fetchedEntities = await getListingEntities(`${commuteLocation.lat}, ${commuteLocation.lng}`);
|
|
193
|
+
}
|
|
190
194
|
setListingEntities(fetchedEntities);
|
|
191
|
-
|
|
195
|
+
// Update travelTime on listings
|
|
196
|
+
const newFilteredListings: Listing[] = [...filteredListings];
|
|
192
197
|
for (let i = 0; i < allListings.length; i++) {
|
|
193
198
|
const listing = newFilteredListings[i];
|
|
194
199
|
if (
|
|
195
200
|
listing &&
|
|
196
201
|
listing.fields &&
|
|
197
|
-
listing.
|
|
198
|
-
listing.
|
|
202
|
+
listing.fields.entityKey &&
|
|
203
|
+
listing.fields.entityKey !== ''
|
|
199
204
|
) {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
const entityKey = listing.fields.entityKey;
|
|
206
|
+
// Try exact match first, then lowercase match for case-insensitive lookup
|
|
207
|
+
const travelTime = (fetchedEntities[entityKey] || fetchedEntities[entityKey.toLowerCase()])?.travelTime;
|
|
203
208
|
if (travelTime !== undefined && listing.fields) {
|
|
204
209
|
listing.fields.travelTime = travelTime;
|
|
205
210
|
}
|
|
@@ -211,35 +216,35 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
|
211
216
|
}
|
|
212
217
|
|
|
213
218
|
fetchEntities();
|
|
214
|
-
}, [commuteLocation, allListings, siteConfig.companyId]);
|
|
219
|
+
}, [commuteLocation, allListings, siteConfig.companyId, getListingEntitiesCallback, noEntities]);
|
|
215
220
|
|
|
216
221
|
useEffect(() => {
|
|
217
222
|
const handleFetchListings = async () => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
if (!(getStorageObject(localStorageKey + 'listings', []) || []).length) {
|
|
224
|
+
setLoading(true);
|
|
225
|
+
}
|
|
221
226
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
227
|
+
try {
|
|
228
|
+
const {
|
|
229
|
+
listingsResult,
|
|
230
|
+
entitiesByKey,
|
|
231
|
+
distinctItems
|
|
232
|
+
} = await fetchListings(commuteLocation, entities, listings, getListingEntitiesCallback, noEntities);
|
|
233
|
+
if (defaultFilters) {
|
|
234
|
+
const filteredListings = listingsResult.filter(listing => {
|
|
235
|
+
if (!listing.fields) return false;
|
|
231
236
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
});
|
|
237
|
+
return Object.keys(defaultFilters).every(filterKey => {
|
|
238
|
+
const filterValues = defaultFilters[filterKey as keyof typeof defaultFilters];
|
|
239
|
+
const listingValue = listing.fields ? listing.fields[filterKey as keyof typeof listing.fields] : null;
|
|
240
|
+
return filterValues.includes(listingValue);
|
|
237
241
|
});
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
});
|
|
243
|
+
setAllListings(filteredListings);
|
|
244
|
+
} else {
|
|
245
|
+
setAllListings(listingsResult);
|
|
246
|
+
}
|
|
247
|
+
setListingEntities(entitiesByKey);
|
|
243
248
|
setMapItems(distinctItems);
|
|
244
249
|
} catch (error) {
|
|
245
250
|
console.log(error);
|
|
@@ -250,12 +255,12 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
|
250
255
|
}, [query, siteConfig, commuteLocation]);
|
|
251
256
|
|
|
252
257
|
useEffect(() => {
|
|
253
|
-
const processListings = () => {
|
|
258
|
+
const processListings = async () => {
|
|
254
259
|
let filteredListings: Listing[];
|
|
255
260
|
let tempSelectedFilters = selectedFilters;
|
|
256
261
|
let tempQuery = query;
|
|
257
262
|
|
|
258
|
-
const { mapItems, filteredListings: tempFilteredListings } = applyFilters(
|
|
263
|
+
const { mapItems, filteredListings: tempFilteredListings } = await applyFilters(
|
|
259
264
|
allListings,
|
|
260
265
|
tempSelectedFilters,
|
|
261
266
|
tempQuery,
|
|
@@ -369,7 +374,8 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
|
|
|
369
374
|
defaultFilters,
|
|
370
375
|
hiddenFilters,
|
|
371
376
|
containerStyle,
|
|
372
|
-
ExpandListComponent
|
|
377
|
+
ExpandListComponent,
|
|
378
|
+
noEntities
|
|
373
379
|
}}>
|
|
374
380
|
{children}
|
|
375
381
|
</MapListContext.Provider>
|
|
@@ -6,7 +6,8 @@ import { ListingEntity } from '~/types/ListingEntity';
|
|
|
6
6
|
|
|
7
7
|
interface FetchListingsResult {
|
|
8
8
|
listingsResult: Listing[];
|
|
9
|
-
|
|
9
|
+
entitiesByKey: Record<string, ListingEntity>;
|
|
10
|
+
|
|
10
11
|
distinctItems: any; // Update this type based on the return type of getDistinctItemsByProximity
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -14,39 +15,46 @@ const fetchListings = async (
|
|
|
14
15
|
commuteLocation: any | null = null,
|
|
15
16
|
entities: ListingEntity[] | null,
|
|
16
17
|
listings: Listing[] | null,
|
|
17
|
-
getListingEntitiesCallback?: (
|
|
18
|
+
getListingEntitiesCallback?: (origin?: string) => Promise<ListingEntity[]>,
|
|
19
|
+
noEntities: boolean = false
|
|
18
20
|
): Promise<FetchListingsResult> => {
|
|
19
21
|
try {
|
|
20
22
|
const listingsResult = listings && listings.length > 0 ? listings : await getListings();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
|
|
23
|
+
|
|
24
|
+
// Only fetch entities if noEntities is false
|
|
25
|
+
const fetchedEntities = noEntities ? {} : (
|
|
26
|
+
!commuteLocation
|
|
27
|
+
? entities && entities.length > 0 ? entities : !getListingEntitiesCallback ? await getListingEntities() : await getListingEntitiesCallback()
|
|
28
|
+
: !getListingEntitiesCallback ? await getListingEntities(
|
|
29
|
+
`${commuteLocation.lat}, ${commuteLocation.lng}`
|
|
30
|
+
) : await getListingEntitiesCallback(
|
|
31
|
+
`${commuteLocation.lat}, ${commuteLocation.lng}`)
|
|
32
|
+
);
|
|
33
|
+
const entitiesByKey = fetchedEntities;
|
|
34
|
+
// Update travel time only if entities were fetched
|
|
35
|
+
if (!noEntities && entitiesByKey) {
|
|
36
|
+
for (let i = 0; i < listingsResult.length; i++) {
|
|
37
|
+
const listing = listingsResult[i];
|
|
38
|
+
if (listing.fields && listing.fields.entityKey && listing.fields.entityKey !== '' && listing.fields) {
|
|
39
|
+
// Try exact match first, then lowercase match for case-insensitive lookup
|
|
40
|
+
const entity = entitiesByKey[listing.fields.entityKey] || entitiesByKey[listing.fields.entityKey.toLowerCase()];
|
|
41
|
+
console.log("Entity for listing with travel time", listing.fields.entityKey, entity);
|
|
42
|
+
|
|
43
|
+
if (entity) {
|
|
44
|
+
listing.fields.travelTime = entity.travelTime;
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
const distinctItems = getDistinctItemsByProximity(
|
|
43
51
|
listingsResult,
|
|
44
|
-
|
|
52
|
+
entitiesByKey || {}
|
|
45
53
|
);
|
|
46
54
|
|
|
47
55
|
return {
|
|
48
56
|
listingsResult,
|
|
49
|
-
|
|
57
|
+
entitiesByKey,
|
|
50
58
|
distinctItems
|
|
51
59
|
};
|
|
52
60
|
} catch (error) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import api from '~/apis/hcApi';
|
|
2
|
-
import { ListingEntity } from '~/types/ListingEntity';
|
|
3
2
|
|
|
4
|
-
export const getListingEntities = async (
|
|
3
|
+
export const getListingEntities = async (origin = ''): Promise<any> => {
|
|
5
4
|
try {
|
|
6
|
-
|
|
5
|
+
//need to update / or add better endpoint fo this to match original functioanlity
|
|
6
|
+
const response = await api.get<any>(`/listingentities/MapEntities?origin=${origin}`);
|
|
7
7
|
return response;
|
|
8
8
|
} catch (error) {
|
|
9
9
|
console.error("Error fetching listing entities:", error);
|
|
@@ -5,18 +5,8 @@ import { Listing } from '../types/Listings';
|
|
|
5
5
|
|
|
6
6
|
export const getListings = async (params?: GetListingsParams): Promise<Listing[]> => {
|
|
7
7
|
try {
|
|
8
|
-
const
|
|
8
|
+
const response = await api.get(`/joblistings/maplistings`);
|
|
9
9
|
|
|
10
|
-
if (params) {
|
|
11
|
-
if (params.location) params.location.forEach(loc => query.append('location', loc));
|
|
12
|
-
if (params.category) params.category.forEach(cat => query.append('category', cat));
|
|
13
|
-
if (params.categoryClass) params.categoryClass.forEach(catClass => query.append('categoryClass', catClass));
|
|
14
|
-
if (params.education) params.education.forEach(edu => query.append('education', edu));
|
|
15
|
-
if (params.city) params.city.forEach(cty => query.append('city', cty));
|
|
16
|
-
if (params.state) params.state.forEach(st => query.append('state', st));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const response = await api.get(`/Listings?${query.toString()}`);
|
|
20
10
|
return response as Listing[];
|
|
21
11
|
} catch (error) {
|
|
22
12
|
console.error(error);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
@config "../../tailwind.config.js";
|
|
2
|
+
|
|
3
|
+
/* Only include components and utilities - no base styles */
|
|
4
|
+
@tailwind components;
|
|
5
|
+
@tailwind utilities;
|
|
6
|
+
|
|
7
|
+
@layer components {
|
|
8
|
+
.track * {
|
|
9
|
+
@apply hc-pointer-events-none;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.stretched-link::after {
|
|
13
|
+
@apply hc-content-[''] hc-absolute hc-inset-0 hc-z-[1] hc-pointer-events-auto hc-bg-transparent;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Component-specific utilities */
|
|
18
|
+
.fit-content {
|
|
19
|
+
height: fit-content;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* CSS Variables for theming - these should be set by the consuming app */
|
|
23
|
+
:root {
|
|
24
|
+
--ui-text: #000000;
|
|
25
|
+
--ui-accent: #959595;
|
|
26
|
+
--primary: #959595;
|
|
27
|
+
--primary-dark: #959595;
|
|
28
|
+
--secondary: #959595;
|
|
29
|
+
--secondary-dark: #959595;
|
|
30
|
+
}
|
|
@@ -2,9 +2,10 @@ export type ListingFields = {
|
|
|
2
2
|
posted?: string;
|
|
3
3
|
subTitle?: string;
|
|
4
4
|
education?: string;
|
|
5
|
-
|
|
5
|
+
title?: string;
|
|
6
6
|
category?: string;
|
|
7
|
-
|
|
7
|
+
subCategory?: string;
|
|
8
|
+
applyUrl?: string;
|
|
8
9
|
shift?: string;
|
|
9
10
|
custom1?: string;
|
|
10
11
|
custom2?: string;
|
|
@@ -22,5 +23,6 @@ export type ListingFields = {
|
|
|
22
23
|
useClientJobUrl?: boolean;
|
|
23
24
|
dateCreated: Date;
|
|
24
25
|
dateLastEdited?: Date;
|
|
26
|
+
entityKey: string;
|
|
25
27
|
travelTime?: string;
|
|
26
28
|
}
|
package/src/types/Listings.ts
CHANGED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { liteClient as algoliasearch } from 'algoliasearch/lite';
|
|
2
|
+
|
|
3
|
+
let algoliaClient = null;
|
|
4
|
+
let algoliaIndexName = null;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Initialize Algolia search client
|
|
8
|
+
* @param {string} appId - Algolia Application ID
|
|
9
|
+
* @param {string} apiKey - Algolia Search API Key
|
|
10
|
+
* @param {string} indexName - Algolia Index Name
|
|
11
|
+
*/
|
|
12
|
+
export const initializeAlgoliaSearch = (appId, apiKey, indexName) => {
|
|
13
|
+
if (!appId || !apiKey || !indexName) {
|
|
14
|
+
console.warn('Algolia search not initialized: missing configuration');
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
algoliaClient = algoliasearch(appId, apiKey);
|
|
20
|
+
algoliaIndexName = indexName;
|
|
21
|
+
return true;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error('Failed to initialize Algolia:', error);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if Algolia is initialized and available
|
|
30
|
+
*/
|
|
31
|
+
export const isAlgoliaAvailable = () => {
|
|
32
|
+
return algoliaClient !== null && algoliaIndexName !== null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Search using Algolia and return matching listing IDs
|
|
37
|
+
* @param {string} query - Search query
|
|
38
|
+
* @returns {Promise<number[]>} Array of listing IDs that match the search
|
|
39
|
+
*/
|
|
40
|
+
export const searchAlgolia = async (query) => {
|
|
41
|
+
if (!isAlgoliaAvailable()) {
|
|
42
|
+
throw new Error('Algolia search is not initialized');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const { results } = await algoliaClient.search({
|
|
47
|
+
requests: [
|
|
48
|
+
{
|
|
49
|
+
indexName: algoliaIndexName,
|
|
50
|
+
query: query,
|
|
51
|
+
hitsPerPage: 1000 // Adjust based on your needs
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Extract IDs from the results
|
|
57
|
+
const hits = results[0]?.hits || [];
|
|
58
|
+
const listingIds = hits.map(hit => {
|
|
59
|
+
// Algolia objectID might be the listing ID, or it might be in a field
|
|
60
|
+
// Adjust this based on your Algolia index structure
|
|
61
|
+
return hit.id || parseInt(hit.objectID);
|
|
62
|
+
}).filter(id => !isNaN(id));
|
|
63
|
+
|
|
64
|
+
return listingIds;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('Algolia search failed:', error);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Filter listings by Algolia search results
|
|
73
|
+
* @param {Array} listings - All listings to filter
|
|
74
|
+
* @param {string} query - Search query
|
|
75
|
+
* @returns {Promise<Array>} Filtered listings
|
|
76
|
+
*/
|
|
77
|
+
export const filterListingsByAlgoliaSearch = async (listings, query) => {
|
|
78
|
+
if (!query || !isAlgoliaAvailable()) {
|
|
79
|
+
return listings;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const matchingIds = await searchAlgolia(query);
|
|
84
|
+
|
|
85
|
+
// Filter listings to only include those with matching IDs
|
|
86
|
+
return listings.filter(listing => matchingIds.includes(listing.id));
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error('Failed to filter by Algolia search, returning all listings:', error);
|
|
89
|
+
return listings;
|
|
90
|
+
}
|
|
91
|
+
};
|
package/src/util/filterUtil.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-undef */
|
|
2
2
|
import { getDistinctItemsByProximity } from '~/util/mapUtil';
|
|
3
|
+
import { isAlgoliaAvailable, filterListingsByAlgoliaSearch } from '~/util/algoliaSearchUtil';
|
|
3
4
|
|
|
4
5
|
import Fuse from 'fuse.js';
|
|
5
6
|
|
|
@@ -82,17 +83,17 @@ export const generateFilterOptions = (
|
|
|
82
83
|
if (fieldName === parentField && filterOptions?.filters) {
|
|
83
84
|
return filterOptions.filters.find(filter => filter.id === fieldName);
|
|
84
85
|
}
|
|
85
|
-
if(fieldName == '
|
|
86
|
+
if(fieldName == 'category'){
|
|
86
87
|
return {
|
|
87
88
|
id: fieldName,
|
|
88
89
|
title: siteConfig.fieldNames[fieldName],
|
|
89
90
|
items: getFilterOptions(allListings, allListings, fieldName)
|
|
90
91
|
};
|
|
91
92
|
}
|
|
92
|
-
if(fieldName == '
|
|
93
|
-
const
|
|
93
|
+
if(fieldName == 'subCategory' && selectedFilters.category){
|
|
94
|
+
const categoryKeys = Object.keys(selectedFilters.category);
|
|
94
95
|
const filteredListings = allListings.filter(
|
|
95
|
-
x =>
|
|
96
|
+
x => categoryKeys.includes(x.fields?.category)
|
|
96
97
|
);
|
|
97
98
|
return {
|
|
98
99
|
id: fieldName,
|
|
@@ -195,7 +196,7 @@ export const generateFilterOptions = (
|
|
|
195
196
|
return null;
|
|
196
197
|
};
|
|
197
198
|
|
|
198
|
-
export const applyFilters = (
|
|
199
|
+
export const applyFilters = async (
|
|
199
200
|
allListings,
|
|
200
201
|
selectedFilters,
|
|
201
202
|
query,
|
|
@@ -239,7 +240,17 @@ export const applyFilters = (
|
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
242
|
if (query) {
|
|
242
|
-
|
|
243
|
+
// Use Algolia if available, otherwise fall back to Fuse.js
|
|
244
|
+
if (isAlgoliaAvailable()) {
|
|
245
|
+
try {
|
|
246
|
+
results = await filterListingsByAlgoliaSearch(results, query);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error('Algolia search failed, falling back to Fuse.js:', error);
|
|
249
|
+
results = searchResults(results, query);
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
results = searchResults(results, query);
|
|
253
|
+
}
|
|
243
254
|
}
|
|
244
255
|
const distinctItems = getDistinctItemsByProximity(results, listingEntities);
|
|
245
256
|
if (hasFavorite) {
|
|
@@ -255,9 +266,9 @@ function searchResults(results, query) {
|
|
|
255
266
|
'fields.posted',
|
|
256
267
|
'fields.subtitle',
|
|
257
268
|
'fields.education',
|
|
258
|
-
'fields.
|
|
269
|
+
'fields.title',
|
|
259
270
|
'fields.category',
|
|
260
|
-
'fields.
|
|
271
|
+
'fields.subCategory',
|
|
261
272
|
'fields.shift',
|
|
262
273
|
'fields.citystate',
|
|
263
274
|
'fields.city',
|