Skip to content

Commit 4d537e1

Browse files
committed
license tag filtering
1 parent 8696a2e commit 4d537e1

4 files changed

Lines changed: 186 additions & 19 deletions

File tree

src/app/[locale]/feeds/components/FeedsScreen.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export default function FeedsScreen(): React.ReactElement {
5858
features: selectedFeatures,
5959
gbfsVersions: selectedGbfsVersions,
6060
licenses: selectedLicenses,
61+
licenseTags: selectedLicenseTags,
6162
hasTransitFeedsRedirect: hasTransitFeedsRedirectParam,
6263
} = derivedPageState;
6364

@@ -95,6 +96,7 @@ export default function FeedsScreen(): React.ReactElement {
9596
features: selectedFeatures,
9697
gbfsVersions: selectedGbfsVersions,
9798
licenses: selectedLicenses,
99+
licenseTags: selectedLicenseTags,
98100
utmSource,
99101
...overrides,
100102
});
@@ -109,6 +111,7 @@ export default function FeedsScreen(): React.ReactElement {
109111
selectedFeatures,
110112
selectedGbfsVersions,
111113
selectedLicenses,
114+
selectedLicenseTags,
112115
utmSource,
113116
router,
114117
],
@@ -134,6 +137,7 @@ export default function FeedsScreen(): React.ReactElement {
134137
features: [],
135138
gbfsVersions: [],
136139
licenses: [],
140+
licenseTags: [],
137141
isOfficial: false,
138142
});
139143
}
@@ -288,6 +292,7 @@ export default function FeedsScreen(): React.ReactElement {
288292
selectedFeatures={selectedFeatures}
289293
selectedGbfsVersions={selectedGbfsVersions}
290294
selectedLicenses={selectedLicenses}
295+
selectedLicenseTags={selectedLicenseTags}
291296
setSelectedFeedTypes={(feedTypes) => {
292297
navigate({ feedTypes: { ...feedTypes }, page: 1 });
293298
}}
@@ -303,6 +308,9 @@ export default function FeedsScreen(): React.ReactElement {
303308
setSelectedLicenses={(licenses) => {
304309
navigate({ licenses, page: 1 });
305310
}}
311+
setSelectedLicenseTags={(licenseTags) => {
312+
navigate({ licenseTags, page: 1 });
313+
}}
306314
isOfficialTagFilterEnabled={isOfficialTagFilterEnabled}
307315
areFeatureFiltersEnabled={areFeatureFiltersEnabled}
308316
areGBFSFiltersEnabled={areGBFSFiltersEnabled}
@@ -417,9 +425,27 @@ export default function FeedsScreen(): React.ReactElement {
417425
/>
418426
))}
419427

428+
{selectedLicenseTags.map((licenseTag) => (
429+
<Chip
430+
color='primary'
431+
variant='outlined'
432+
size='small'
433+
label={licenseTag}
434+
key={licenseTag}
435+
onDelete={() => {
436+
navigate({
437+
licenseTags: selectedLicenseTags.filter(
438+
(lt) => lt !== licenseTag,
439+
),
440+
});
441+
}}
442+
/>
443+
))}
444+
420445
{(selectedFeatures.length > 0 ||
421446
selectedGbfsVersions.length > 0 ||
422447
selectedLicenses.length > 0 ||
448+
selectedLicenseTags.length > 0 ||
423449
isOfficialFeedSearch ||
424450
selectedFeedTypes.gtfs_rt ||
425451
selectedFeedTypes.gtfs ||
@@ -553,6 +579,7 @@ export default function FeedsScreen(): React.ReactElement {
553579
feedsData={feedsData}
554580
selectedFeatures={selectedFeatures}
555581
selectedGbfsVersions={selectedGbfsVersions}
582+
selectedLicenseTags={selectedLicenseTags}
556583
isLoadingFeeds={isLoading || isValidating}
557584
/>
558585
)}

src/app/[locale]/feeds/lib/useFeedsSearch.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export function deriveSearchParams(searchParams: URLSearchParams): {
6363
features: string[];
6464
gbfsVersions: string[];
6565
licenses: string[];
66+
licenseTags: string[];
6667
hasTransitFeedsRedirect: boolean;
6768
} {
6869
const feedTypes = getInitialSelectedFeedTypes(searchParams);
@@ -76,6 +77,8 @@ export function deriveSearchParams(searchParams: URLSearchParams): {
7677
gbfsVersions:
7778
searchParams.get('gbfs_versions')?.split(',').filter(Boolean) ?? [],
7879
licenses: searchParams.get('licenses')?.split(',').filter(Boolean) ?? [],
80+
licenseTags:
81+
searchParams.get('license_tags')?.split(',').filter(Boolean) ?? [],
7982
hasTransitFeedsRedirect: searchParams.get('utm_source') === 'transitfeeds',
8083
};
8184
}
@@ -116,6 +119,7 @@ function buildSwrKey(derived: ReturnType<typeof deriveSearchParams>): string {
116119
features,
117120
gbfsVersions,
118121
licenses,
122+
licenseTags,
119123
} = derived;
120124
const flags = deriveFilterFlags(feedTypes);
121125
const cacheWindow = Math.floor(Date.now() / CACHE_TTL_MS);
@@ -138,6 +142,9 @@ function buildSwrKey(derived: ReturnType<typeof deriveSearchParams>): string {
138142
if (licenses.length > 0) {
139143
params.set('licenses', licenses.join(','));
140144
}
145+
if (licenseTags.length > 0) {
146+
params.set('license_tags', licenseTags.join(','));
147+
}
141148
return `feeds-search?${params.toString()}`;
142149
}
143150

@@ -156,6 +163,7 @@ async function feedsFetcher(
156163
features,
157164
gbfsVersions,
158165
licenses,
166+
licenseTags,
159167
} = derivedSearchParams;
160168
const flags = deriveFilterFlags(feedTypes);
161169
const offset = (page - 1) * SEARCH_LIMIT;
@@ -175,6 +183,7 @@ async function feedsFetcher(
175183
? gbfsVersions.join(',').replaceAll('v', '')
176184
: undefined,
177185
license_ids: licenses.length > 0 ? licenses.join(',') : undefined,
186+
license_tags: licenseTags.length > 0 ? licenseTags.join(',') : undefined,
178187
},
179188
};
180189

@@ -249,6 +258,7 @@ export function buildSearchUrl(
249258
features?: string[];
250259
gbfsVersions?: string[];
251260
licenses?: string[];
261+
licenseTags?: string[];
252262
utmSource?: string | null;
253263
},
254264
): string {
@@ -278,6 +288,9 @@ export function buildSearchUrl(
278288
if (filters.licenses != null && filters.licenses.length > 0) {
279289
params.set('licenses', filters.licenses.join(','));
280290
}
291+
if (filters.licenseTags != null && filters.licenseTags.length > 0) {
292+
params.set('license_tags', filters.licenseTags.join(','));
293+
}
281294
if (filters.isOfficial === true) {
282295
params.set('official', 'true');
283296
}

src/app/screens/Feeds/AdvancedSearchTable.tsx

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import * as React from 'react';
2020
import { FeedStatusIndicator } from '../../components/FeedStatus';
2121
import { useTranslations } from 'next-intl';
2222
import LockIcon from '@mui/icons-material/Lock';
23+
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
2324
import GtfsRtEntities from './GtfsRtEntities';
2425
import { getEmojiFlag, type TCountryCode } from 'countries-list';
2526
import OfficialChip from '../../components/OfficialChip';
@@ -33,18 +34,28 @@ export interface AdvancedSearchTableProps {
3334
feedsData: AllFeedsType | undefined;
3435
selectedFeatures: string[] | undefined;
3536
selectedGbfsVersions: string[] | undefined;
37+
selectedLicenseTags: string[] | undefined;
3638
isLoadingFeeds: boolean;
3739
}
3840

3941
interface DetailsContainerProps {
4042
children: React.ReactNode;
4143
feedSearchItem: SearchFeedItem;
44+
selectedLicenseTags: string[];
4245
}
4346

4447
const DetailsContainer = ({
4548
children,
4649
feedSearchItem,
50+
selectedLicenseTags,
4751
}: DetailsContainerProps): React.ReactElement => {
52+
const licenseTags = feedSearchItem.source_info?.license_tags;
53+
const licenseTagsTitle =
54+
licenseTags != null && licenseTags.length > 0
55+
? licenseTags.join(', ')
56+
: undefined;
57+
const matchingTags =
58+
licenseTags?.filter((tag) => selectedLicenseTags.includes(tag)) ?? [];
4859
return (
4960
<Box
5061
sx={{
@@ -55,20 +66,52 @@ const DetailsContainer = ({
5566
}}
5667
>
5768
<Box sx={{ width: 'calc(100% - 100px' }}>{children}</Box>
58-
<Tooltip title={'Feed License'} placement={'top-end'}>
59-
<Typography
60-
variant='caption'
61-
sx={{
62-
opacity: 0.7,
63-
ml: { xs: 0, lg: 2 },
64-
minWidth: { xs: '100%', lg: '100px' },
65-
textAlign: 'right',
66-
pt: { xs: 1, lg: 0 },
67-
}}
69+
<Box
70+
sx={{
71+
display: 'flex',
72+
alignItems: 'center',
73+
gap: 0.5,
74+
ml: { xs: 0, lg: 2 },
75+
minWidth: { xs: '100%', lg: '100px' },
76+
justifyContent: 'flex-end',
77+
flexWrap: 'wrap',
78+
pt: { xs: 1, lg: 0 },
79+
}}
80+
>
81+
{matchingTags.map((tag) => (
82+
<Chip
83+
key={tag}
84+
label={tag}
85+
size='small'
86+
color='primary'
87+
variant='outlined'
88+
sx={{ opacity: 0.8 }}
89+
/>
90+
))}
91+
<Tooltip
92+
title={licenseTagsTitle ?? ''}
93+
placement='top-end'
94+
disableTouchListener={licenseTagsTitle == null}
6895
>
69-
{feedSearchItem.source_info?.license_id}
70-
</Typography>
71-
</Tooltip>
96+
<Box
97+
sx={{
98+
display: 'flex',
99+
alignItems: 'center',
100+
gap: 0.5,
101+
}}
102+
>
103+
{licenseTagsTitle != null && (
104+
<InfoOutlinedIcon sx={{ fontSize: 14, opacity: 0.7 }} />
105+
)}
106+
<Typography
107+
variant='caption'
108+
sx={{ opacity: 0.7, textAlign: 'right' }}
109+
>
110+
{feedSearchItem.source_info?.license_id}
111+
</Typography>
112+
</Box>
113+
</Tooltip>
114+
</Box>
72115
</Box>
73116
);
74117
};
@@ -77,11 +120,15 @@ const renderGTFSDetails = (
77120
gtfsFeed: SearchFeedItem,
78121
selectedFeatures: string[],
79122
theme: Theme,
123+
selectedLicenseTags: string[],
80124
): React.ReactElement => {
81125
const feedFeatures =
82126
gtfsFeed?.latest_dataset?.validation_report?.features ?? [];
83127
return (
84-
<DetailsContainer feedSearchItem={gtfsFeed}>
128+
<DetailsContainer
129+
feedSearchItem={gtfsFeed}
130+
selectedLicenseTags={selectedLicenseTags}
131+
>
85132
{gtfsFeed?.feed_name != null && (
86133
<Typography
87134
variant='body1'
@@ -123,9 +170,13 @@ const renderGTFSDetails = (
123170

124171
const renderGTFSRTDetails = (
125172
gtfsRtFeed: SearchFeedItem,
173+
selectedLicenseTags: string[],
126174
): React.ReactElement => {
127175
return (
128-
<DetailsContainer feedSearchItem={gtfsRtFeed}>
176+
<DetailsContainer
177+
feedSearchItem={gtfsRtFeed}
178+
selectedLicenseTags={selectedLicenseTags}
179+
>
129180
<GtfsRtEntities
130181
entities={gtfsRtFeed?.entity_types}
131182
includeName={true}
@@ -138,9 +189,13 @@ const renderGBFSDetails = (
138189
gbfsFeedSearchElement: SearchFeedItem,
139190
selectedGbfsVersions: string[],
140191
theme: Theme,
192+
selectedLicenseTags: string[],
141193
): React.ReactElement => {
142194
return (
143-
<DetailsContainer feedSearchItem={gbfsFeedSearchElement}>
195+
<DetailsContainer
196+
feedSearchItem={gbfsFeedSearchElement}
197+
selectedLicenseTags={selectedLicenseTags}
198+
>
144199
{gbfsFeedSearchElement.versions?.map((version: string, index: number) => (
145200
<Chip
146201
label={'v' + version}
@@ -164,6 +219,7 @@ export default function AdvancedSearchTable({
164219
feedsData,
165220
selectedFeatures,
166221
selectedGbfsVersions,
222+
selectedLicenseTags,
167223
isLoadingFeeds,
168224
}: AdvancedSearchTableProps): React.ReactElement {
169225
const t = useTranslations('feeds');
@@ -390,18 +446,28 @@ export default function AdvancedSearchTable({
390446
: {}
391447
}
392448
>
393-
{renderGTFSDetails(feed, selectedFeatures ?? [], theme)}
449+
{renderGTFSDetails(
450+
feed,
451+
selectedFeatures ?? [],
452+
theme,
453+
selectedLicenseTags ?? [],
454+
)}
394455
</Box>
395456
)}
396457
{feed.data_type === 'gtfs_rt' && (
397458
<Box sx={descriptionDividerStyle}>
398-
{renderGTFSRTDetails(feed)}
459+
{renderGTFSRTDetails(feed, selectedLicenseTags ?? [])}
399460
</Box>
400461
)}
401462

402463
{feed.data_type === 'gbfs' && (
403464
<Box sx={hasGbfsVersions ? descriptionDividerStyle : {}}>
404-
{renderGBFSDetails(feed, selectedGbfsVersions ?? [], theme)}
465+
{renderGBFSDetails(
466+
feed,
467+
selectedGbfsVersions ?? [],
468+
theme,
469+
selectedLicenseTags ?? [],
470+
)}
405471
</Box>
406472
)}
407473
</Box>

0 commit comments

Comments
 (0)