Skip to content

Commit 1b3ba6e

Browse files
filter by license (#1595)
1 parent 0ac9c92 commit 1b3ba6e

File tree

3 files changed

+160
-8
lines changed

3 files changed

+160
-8
lines changed

web-app/src/app/screens/Feeds/AdvancedSearchTable.tsx

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,43 @@ export interface AdvancedSearchTableProps {
3131
selectedGbfsVersions: string[] | undefined;
3232
}
3333

34+
interface DetailsContainerProps {
35+
children: React.ReactNode;
36+
feedSearchItem: SearchFeedItem;
37+
}
38+
39+
const DetailsContainer = ({
40+
children,
41+
feedSearchItem,
42+
}: DetailsContainerProps): React.ReactElement => {
43+
return (
44+
<Box
45+
sx={{
46+
display: 'flex',
47+
justifyContent: 'space-between',
48+
alignItems: 'end',
49+
flexWrap: { xs: 'wrap', lg: 'nowrap' },
50+
}}
51+
>
52+
<Box sx={{ width: 'calc(100% - 100px' }}>{children}</Box>
53+
<Tooltip title={'Feed License'} placement={'top-end'}>
54+
<Typography
55+
variant='caption'
56+
sx={{
57+
opacity: 0.7,
58+
ml: { xs: 0, lg: 2 },
59+
minWidth: { xs: '100%', lg: '100px' },
60+
textAlign: 'right',
61+
pt: { xs: 1, lg: 0 },
62+
}}
63+
>
64+
{feedSearchItem.source_info?.license_id}
65+
</Typography>
66+
</Tooltip>
67+
</Box>
68+
);
69+
};
70+
3471
const renderGTFSDetails = (
3572
gtfsFeed: SearchFeedItem,
3673
selectedFeatures: string[],
@@ -39,7 +76,7 @@ const renderGTFSDetails = (
3976
const feedFeatures =
4077
gtfsFeed?.latest_dataset?.validation_report?.features ?? [];
4178
return (
42-
<>
79+
<DetailsContainer feedSearchItem={gtfsFeed}>
4380
{gtfsFeed?.feed_name != null && (
4481
<Typography
4582
variant='body1'
@@ -75,18 +112,20 @@ const renderGTFSDetails = (
75112
},
76113
)}
77114
</Box>
78-
</>
115+
</DetailsContainer>
79116
);
80117
};
81118

82119
const renderGTFSRTDetails = (
83120
gtfsRtFeed: SearchFeedItem,
84121
): React.ReactElement => {
85122
return (
86-
<GtfsRtEntities
87-
entities={gtfsRtFeed?.entity_types}
88-
includeName={true}
89-
></GtfsRtEntities>
123+
<DetailsContainer feedSearchItem={gtfsRtFeed}>
124+
<GtfsRtEntities
125+
entities={gtfsRtFeed?.entity_types}
126+
includeName={true}
127+
></GtfsRtEntities>
128+
</DetailsContainer>
90129
);
91130
};
92131

@@ -96,7 +135,7 @@ const renderGBFSDetails = (
96135
): JSX.Element => {
97136
const theme = useTheme();
98137
return (
99-
<Box>
138+
<DetailsContainer feedSearchItem={gbfsFeedSearchElement}>
100139
{gbfsFeedSearchElement.versions?.map((version: string, index: number) => (
101140
<Chip
102141
label={'v' + version}
@@ -112,7 +151,7 @@ const renderGBFSDetails = (
112151
}}
113152
/>
114153
))}
115-
</Box>
154+
</DetailsContainer>
116155
);
117156
};
118157

web-app/src/app/screens/Feeds/SearchFilters.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ interface SearchFiltersProps {
2525
isOfficialFeedSearch: boolean;
2626
selectedFeatures: string[];
2727
selectedGbfsVersions: string[];
28+
selectedLicenses: string[];
2829
setSelectedFeedTypes: (selectedFeedTypes: Record<string, boolean>) => void;
2930
setIsOfficialFeedSearch: (isOfficialFeedSearch: boolean) => void;
3031
setSelectedFeatures: (selectedFeatures: string[]) => void;
3132
setSelectedGbfsVerions: (selectedVersions: string[]) => void;
33+
setSelectedLicenses: (selectedLicenses: string[]) => void;
3234
isOfficialTagFilterEnabled: boolean;
3335
areFeatureFiltersEnabled: boolean;
3436
areGBFSFiltersEnabled: boolean;
@@ -39,10 +41,12 @@ export function SearchFilters({
3941
isOfficialFeedSearch,
4042
selectedFeatures,
4143
selectedGbfsVersions,
44+
selectedLicenses,
4245
setSelectedFeedTypes,
4346
setIsOfficialFeedSearch,
4447
setSelectedFeatures,
4548
setSelectedGbfsVerions,
49+
setSelectedLicenses,
4650
isOfficialTagFilterEnabled,
4751
areFeatureFiltersEnabled,
4852
areGBFSFiltersEnabled,
@@ -58,6 +62,7 @@ export function SearchFilters({
5862
features: areFeatureFiltersEnabled,
5963
tags: isOfficialTagFilterEnabled,
6064
gbfsVersions: true,
65+
licenses: true,
6166
});
6267
const [featureCheckboxData, setFeatureCheckboxData] = useState<
6368
CheckboxStructure[]
@@ -276,6 +281,75 @@ export function SearchFilters({
276281
></NestedCheckboxList>
277282
</AccordionDetails>
278283
</Accordion>
284+
285+
<Accordion
286+
disableGutters
287+
variant={'outlined'}
288+
sx={{
289+
border: 0,
290+
'&::before': {
291+
display: 'none',
292+
},
293+
}}
294+
expanded={expandedCategories.licenses}
295+
onChange={() => {
296+
setExpandedCategories({
297+
...expandedCategories,
298+
licenses: !expandedCategories.licenses,
299+
});
300+
}}
301+
>
302+
<AccordionSummary
303+
expandIcon={<ExpandMoreIcon />}
304+
aria-controls='panel-licenses-content'
305+
sx={{
306+
px: 0,
307+
}}
308+
>
309+
<SearchHeader variant='h6'>Licenses</SearchHeader>
310+
</AccordionSummary>
311+
<AccordionDetails sx={{ p: 0, m: 0, border: 0 }}>
312+
<NestedCheckboxList
313+
debounceTime={500}
314+
checkboxData={[
315+
{
316+
title: 'CC-BY-4.0',
317+
checked: selectedLicenses.includes('CC-BY-4.0'),
318+
type: 'checkbox',
319+
},
320+
{
321+
title: 'etalab-2.0',
322+
checked: selectedLicenses.includes('etalab-2.0'),
323+
type: 'checkbox',
324+
},
325+
{
326+
title: 'CC0-1.0',
327+
checked: selectedLicenses.includes('CC0-1.0'),
328+
type: 'checkbox',
329+
},
330+
{
331+
title: 'ODbL-1.0',
332+
checked: selectedLicenses.includes('ODbL-1.0'),
333+
type: 'checkbox',
334+
},
335+
{
336+
title: 'OGL-UK-3.0',
337+
checked: selectedLicenses.includes('OGL-UK-3.0'),
338+
type: 'checkbox',
339+
},
340+
]}
341+
onCheckboxChange={(checkboxData) => {
342+
const selectedLicenseIds: string[] = [];
343+
checkboxData.forEach((checkbox) => {
344+
if (checkbox.checked) {
345+
selectedLicenseIds.push(checkbox.title);
346+
}
347+
});
348+
setSelectedLicenses([...selectedLicenseIds]);
349+
}}
350+
></NestedCheckboxList>
351+
</AccordionDetails>
352+
</Accordion>
279353
</>
280354
);
281355
}

web-app/src/app/screens/Feeds/index.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ export default function Feed(): React.ReactElement {
6666
const [selectGbfsVersions, setSelectGbfsVersions] = useState<string[]>(
6767
searchParams.get('gbfs_versions')?.split(',') ?? [],
6868
);
69+
const [selectedLicenses, setSelectedLicenses] = useState<string[]>(
70+
searchParams.get('licenses')?.split(',') ?? [],
71+
);
6972
const [activePagination, setActivePagination] = useState(
7073
searchParams.get('o') !== null ? Number(searchParams.get('o')) : 1,
7174
);
@@ -127,6 +130,10 @@ export default function Feed(): React.ReactElement {
127130
version: areGBFSFiltersEnabled
128131
? selectGbfsVersions.join(',').replaceAll('v', '')
129132
: undefined,
133+
license_ids:
134+
selectedLicenses.length > 0
135+
? selectedLicenses.join(',')
136+
: undefined,
130137
},
131138
},
132139
}),
@@ -140,6 +147,7 @@ export default function Feed(): React.ReactElement {
140147
isOfficialFeedSearch,
141148
selectedFeatures,
142149
selectGbfsVersions,
150+
selectedLicenses,
143151
]);
144152

145153
useEffect(() => {
@@ -166,6 +174,9 @@ export default function Feed(): React.ReactElement {
166174
if (selectGbfsVersions.length > 0) {
167175
newSearchParams.set('gbfs_versions', selectGbfsVersions.join(','));
168176
}
177+
if (selectedLicenses.length > 0) {
178+
newSearchParams.set('licenses', selectedLicenses.join(','));
179+
}
169180
if (isOfficialFeedSearch) {
170181
newSearchParams.set('official', 'true');
171182
}
@@ -181,6 +192,7 @@ export default function Feed(): React.ReactElement {
181192
selectedFeedTypes,
182193
selectedFeatures,
183194
selectGbfsVersions,
195+
selectedLicenses,
184196
isOfficialFeedSearch,
185197
]);
186198

@@ -208,6 +220,11 @@ export default function Feed(): React.ReactElement {
208220
setSelectGbfsVersions([...newGbfsVersions]);
209221
}
210222

223+
const newLicenses = searchParams.get('licenses')?.split(',') ?? [];
224+
if (newLicenses.join(',') !== selectedLicenses.join(',')) {
225+
setSelectedLicenses([...newLicenses]);
226+
}
227+
211228
const newSearchOfficial = Boolean(searchParams.get('official')) ?? false;
212229
if (newSearchOfficial !== isOfficialFeedSearch) {
213230
setIsOfficialFeedSearch(newSearchOfficial);
@@ -259,6 +276,7 @@ export default function Feed(): React.ReactElement {
259276
});
260277
setSelectedFeatures([]);
261278
setSelectGbfsVersions([]);
279+
setSelectedLicenses([]);
262280
setIsOfficialFeedSearch(false);
263281
}
264282

@@ -396,6 +414,7 @@ export default function Feed(): React.ReactElement {
396414
isOfficialFeedSearch={isOfficialFeedSearch}
397415
selectedFeatures={selectedFeatures}
398416
selectedGbfsVersions={selectGbfsVersions}
417+
selectedLicenses={selectedLicenses}
399418
setSelectedFeedTypes={(feedTypes) => {
400419
setActivePagination(1);
401420
setSelectedFeedTypes(feedTypes);
@@ -412,6 +431,10 @@ export default function Feed(): React.ReactElement {
412431
setSelectGbfsVersions(versions);
413432
setActivePagination(1);
414433
}}
434+
setSelectedLicenses={(licenses) => {
435+
setActivePagination(1);
436+
setSelectedLicenses(licenses);
437+
}}
415438
isOfficialTagFilterEnabled={isOfficialTagFilterEnabled}
416439
areFeatureFiltersEnabled={areFeatureFiltersEnabled}
417440
areGBFSFiltersEnabled={areGBFSFiltersEnabled}
@@ -511,8 +534,24 @@ export default function Feed(): React.ReactElement {
511534
/>
512535
))}
513536

537+
{selectedLicenses.map((license) => (
538+
<Chip
539+
color='primary'
540+
variant='outlined'
541+
size='small'
542+
label={license}
543+
key={license}
544+
onDelete={() => {
545+
setSelectedLicenses([
546+
...selectedLicenses.filter((sl) => sl !== license),
547+
]);
548+
}}
549+
/>
550+
))}
551+
514552
{(selectedFeatures.length > 0 ||
515553
selectGbfsVersions.length > 0 ||
554+
selectedLicenses.length > 0 ||
516555
isOfficialFeedSearch ||
517556
selectedFeedTypes.gtfs_rt ||
518557
selectedFeedTypes.gtfs ||

0 commit comments

Comments
 (0)