Skip to content

Commit

Permalink
Add filters used to url (#485)
Browse files Browse the repository at this point in the history
Closes #409

Signed-off-by: Cintia Sanchez Garcia <[email protected]>
  • Loading branch information
cynthia-sg authored Feb 5, 2024
1 parent 29dfbc2 commit 9d2d959
Show file tree
Hide file tree
Showing 14 changed files with 409 additions and 87 deletions.
10 changes: 7 additions & 3 deletions web/src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export const MODAL_PARAM = 'modal';
export const ITEM_PARAM = 'item';
export const CATEGORY_PARAM = 'category';
export const SUBCATEGORY_PARAM = 'subcategory';
export const PAGE_PARAM = 'page';
export const FINANCES_KIND_PARAM = 'kind';
export const SORT_BY_PARAM = 'sort-by';
export const SORT_DIRECTION_PARAM = 'sort-direction';

export const REGEX_SPACE = / /g;
export const REGEX_PLUS = /\+/g;
Expand Down Expand Up @@ -109,8 +113,8 @@ export const FILTERS: FilterSection[] = [
],
},
{
value: FilterCategory.CompanyType,
title: 'Company type',
value: FilterCategory.OrgType,
title: 'Organization type',
options: [
{
value: 'for_profit',
Expand All @@ -128,7 +132,7 @@ export const FILTER_CATEGORIES_PER_TITLE: FilterCategoriesPerTitle = {
[FilterTitle.Project]: [FilterCategory.Maturity, FilterCategory.TAG, FilterCategory.License],
[FilterTitle.Organization]: [
FilterCategory.Organization,
FilterCategory.CompanyType,
FilterCategory.OrgType,
FilterCategory.Industry,
FilterCategory.Country,
],
Expand Down
52 changes: 42 additions & 10 deletions web/src/layout/common/ActiveFiltersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import SVGIcon from './SVGIcon';

interface Props {
activeFilters: ActiveFilters;
maturityOptions?: string[];
resetFilters: () => void;
resetFilter?: (name: FilterCategory) => void;
removeFilter: (name: FilterCategory, value: string) => void;
}

Expand Down Expand Up @@ -51,14 +53,44 @@ const ActiveFiltersList = (props: Props) => {
<div class="d-flex flex-row flex-wrap">
<For each={Object.keys(activeFilters())}>
{(f: string) => {
if (isUndefined(props.activeFilters[f as FilterCategory])) return null;
const activeFiltersPerCategory = () => activeFilters()[f as FilterCategory];
if (isUndefined(activeFiltersPerCategory()) || isEmpty(activeFiltersPerCategory())) return null;

const allMatutirySelected = () =>
!isUndefined(props.maturityOptions) &&
f === FilterCategory.Maturity &&
props.maturityOptions.every((element) => activeFiltersPerCategory()!.includes(element));
const foundationLabel = getFoundationNameLabel();

return (
<>
{
<For each={props.activeFilters[f as FilterCategory]}>
<Switch>
<Match when={allMatutirySelected()}>
<span
role="listitem"
class={`badge badge-sm border rounded-0 me-3 my-1 d-flex flex-row align-items-center ${styles.filterBadge}`}
>
<div class="d-flex flex-row align-items-baseline">
<div>
<small class="text-uppercase fw-normal me-2">{f}:</small>
<span class="text-uppercase">{foundationLabel}</span>
</div>
<button
class="btn btn-link btn-sm text-reset lh-1 p-0 ps-2"
onClick={() =>
!isUndefined(props.resetFilter) ? props.resetFilter(f as FilterCategory) : null
}
aria-label={`Remove ${foundationLabel} filter`}
title={`Remove ${foundationLabel} filter`}
>
<SVGIcon kind={SVGIconKind.ClearCircle} />
</button>
</div>
</span>
</Match>

<Match when={!allMatutirySelected()}>
<For each={activeFiltersPerCategory()}>
{(c: string) => {
// Do not render maturity filter when is foundation name
if (f === FilterCategory.Maturity && c === getFoundationNameLabel()) return null;
return (
<span
role="listitem"
Expand All @@ -69,13 +101,13 @@ const ActiveFiltersList = (props: Props) => {
<small class="text-uppercase fw-normal me-2">{f}:</small>
<span
class={
[FilterCategory.Maturity, FilterCategory.CompanyType].includes(f as FilterCategory)
[FilterCategory.Maturity, FilterCategory.OrgType].includes(f as FilterCategory)
? 'text-uppercase'
: ''
}
>
<Switch fallback={<>{c}</>}>
<Match when={f === FilterCategory.CompanyType}>
<Match when={f === FilterCategory.OrgType}>
<>{formatProfitLabel(c)}</>
</Match>
<Match when={f === FilterCategory.TAG}>
Expand All @@ -100,8 +132,8 @@ const ActiveFiltersList = (props: Props) => {
);
}}
</For>
}
</>
</Match>
</Switch>
);
}}
</For>
Expand Down
22 changes: 11 additions & 11 deletions web/src/layout/common/Section.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import isEqual from 'lodash/isEqual';
import intersection from 'lodash/intersection';
import isUndefined from 'lodash/isUndefined';
import { For, Show } from 'solid-js';

Expand All @@ -24,6 +24,7 @@ interface Props {
const Section = (props: Props) => {
const onChange = (name: FilterCategory, value: string, checked: boolean, subOtps?: string[]) => {
let tmpActiveFilters: string[] = props.activeFilters ? [...props.activeFilters] : [];

if (!checked) {
if (props.activeFilters) {
tmpActiveFilters = props.activeFilters.filter((f: string) => f !== value && !(subOtps || []).includes(f));
Expand All @@ -33,17 +34,10 @@ const Section = (props: Props) => {
tmpActiveFilters.push(value);
} else {
tmpActiveFilters = [...tmpActiveFilters, ...subOtps];
tmpActiveFilters.push(value);
tmpActiveFilters = [...new Set(tmpActiveFilters)];
}
}

// Remove foundation value from maturity category when no more options
if (name === FilterCategory.Maturity) {
if (isEqual(tmpActiveFilters, [window.baseDS.foundation.toLowerCase()])) {
tmpActiveFilters = [];
}
}
props.updateActiveFilters(name, tmpActiveFilters);
};

Expand All @@ -60,7 +54,7 @@ const Section = (props: Props) => {
const renderChecksList = () => (
<For each={props.section!.options}>
{(opt: FilterOption) => {
let subOpts: string[];
let subOpts: string[] | undefined;
let suboptions = opt.suboptions;
if (
opt.value === getFoundationNameLabel() &&
Expand All @@ -82,7 +76,11 @@ const Section = (props: Props) => {
class={isUndefined(props.inLine) ? 'my-2' : 'mt-2'}
label={opt.name}
device={props.device}
checked={(props.activeFilters || []).includes(opt.value)}
checked={
!isUndefined(subOpts)
? intersection(subOpts, props.activeFilters || []).length > 0
: (props.activeFilters || []).includes(opt.value)
}
onChange={(value: string, checked: boolean) => onChange(props.section!.value!, value, checked, subOpts)}
/>
<div class="ms-3">
Expand All @@ -97,7 +95,9 @@ const Section = (props: Props) => {
label={subOpt.name}
device={props.device}
checked={(props.activeFilters || []).includes(subOpt.value)}
onChange={(value: string, checked: boolean) => onChange(props.section!.value!, value, checked)}
onChange={(value: string, checked: boolean) => {
onChange(props.section!.value!, value, checked);
}}
/>
)}
</For>
Expand Down
45 changes: 34 additions & 11 deletions web/src/layout/explore/filters/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { intersection } from 'lodash';
import isEmpty from 'lodash/isEmpty';
import isUndefined from 'lodash/isUndefined';
import some from 'lodash/some';
import { Accessor, createEffect, createSignal, on, Show } from 'solid-js';

import { FILTER_CATEGORIES_PER_TITLE, FILTERS } from '../../../data';
import { ActiveFilters, BaseData, FilterCategory, FilterSection, FilterTitle, Item, SVGIconKind } from '../../../types';
import {
ActiveFilters,
BaseData,
FilterCategory,
FilterOption,
FilterSection,
FilterTitle,
Item,
SVGIconKind,
} from '../../../types';
import getFoundationNameLabel from '../../../utils/getFoundationNameLabel';
import prepareData from '../../../utils/prepareData';
import getFiltersPerGroup, { FiltersPerGroup } from '../../../utils/prepareFilters';
import Loading from '../../common/Loading';
Expand All @@ -30,17 +41,35 @@ const Filters = (props: Props) => {
const [filters, setFilters] = createSignal<FilterSection[]>([]);
const [visibleTitles, setVisibleTitles] = createSignal<FilterTitle[]>([]);

// Keep only available filters in selected group filters
const cleanInitialActiveFilters = (): ActiveFilters => {
const cleanFilters: ActiveFilters = {};
Object.keys(props.initialActiveFilters()).forEach((f: string) => {
const filter: FilterCategory = f as FilterCategory;
const currentFilter = filters().find((section: FilterSection) => section.value === filter);
if (currentFilter) {
const opts = currentFilter.options.map((opt: FilterOption) => opt.value);
// Add non-foundation label
if (filter === FilterCategory.Maturity) {
opts.push(`non-${getFoundationNameLabel()}`);
}
cleanFilters[filter] = intersection(opts, props.initialActiveFilters()[filter]);
}
});
return cleanFilters;
};

createEffect(
on(visibleFiltersModal, () => {
if (visibleFiltersModal()) {
setTmpActiveFilters(props.initialActiveFilters);
if (filters().length === 0) {
const f = getFiltersPerGroup(prepareData(props.data, props.initialLandscapeData()!));
if (!isEmpty(f)) {
setFiltersFromData(f);
setFilters(f[props.initialSelectedGroup() || 'default']);
}
}
setTmpActiveFilters(cleanInitialActiveFilters());
}
})
);
Expand Down Expand Up @@ -116,11 +145,6 @@ const Filters = (props: Props) => {
updateActiveFilters(name, []);
};

const resetFilters = () => {
setTmpActiveFilters({});
props.applyFilters({});
};

return (
<>
<div class="position-relative">
Expand Down Expand Up @@ -152,8 +176,7 @@ const Filters = (props: Props) => {
class="btn btn-sm btn-link text-muted py-0"
onClick={(e) => {
e.preventDefault();
resetFilters();
setVisibleFiltersModal(false);
setTmpActiveFilters({});
}}
aria-label="Reset filters"
>
Expand Down Expand Up @@ -253,8 +276,8 @@ const Filters = (props: Props) => {

<Section
title="Type"
section={getSectionInPredefinedFilters(FilterCategory.CompanyType)}
activeFilters={{ ...tmpActiveFilters() }[FilterCategory.CompanyType]}
section={getSectionInPredefinedFilters(FilterCategory.OrgType)}
activeFilters={{ ...tmpActiveFilters() }[FilterCategory.OrgType]}
updateActiveFilters={updateActiveFilters}
resetFilter={resetFilter}
sectionClass={styles.section}
Expand Down
Loading

0 comments on commit 9d2d959

Please sign in to comment.