Fixed filters, connected to algolia
This commit is contained in:
committed by
fabioberger
parent
e922299a55
commit
e7db5aa4f3
@@ -3,46 +3,47 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import { colors } from 'ts/style/colors';
|
import { colors } from 'ts/style/colors';
|
||||||
|
|
||||||
export interface IFilterProps {
|
export interface IFilterProps extends IFilterCheckboxProps {
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
label: string;
|
label: string;
|
||||||
|
value: string;
|
||||||
|
refine: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Filter: React.FC<IFilterProps> = ({ value, name, label }) => (
|
interface IFilterCheckboxProps {
|
||||||
<FilterWrapper>
|
isRefined: boolean;
|
||||||
<FilterInput name={name} />
|
}
|
||||||
<FilterCheckbox />
|
|
||||||
<FilterLabel>{label}</FilterLabel>
|
export const Filter: React.FC<IFilterProps> = ({ isRefined, label, value, refine }) => {
|
||||||
</FilterWrapper>
|
const handleClick = () => refine(value);
|
||||||
);
|
|
||||||
|
return (
|
||||||
|
<FilterWrapper onClick={handleClick}>
|
||||||
|
<FilterCheckbox isRefined={isRefined} />
|
||||||
|
<FilterLabel>{label}</FilterLabel>
|
||||||
|
</FilterWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const FilterWrapper = styled.label`
|
const FilterWrapper = styled.label`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 0.833333333rem;
|
margin-bottom: 0.83rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FilterInput = styled.input.attrs({ type: 'checkbox' })`
|
const FilterCheckbox = styled.div<{ isRefined: boolean }>`
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const FilterCheckbox = styled.div`
|
|
||||||
border: 1px solid #cbcbcb;
|
border: 1px solid #cbcbcb;
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
margin-right: 0.666rem;
|
margin-right: 0.67rem;
|
||||||
|
|
||||||
${FilterInput}:checked ~ & {
|
${({ isRefined }) =>
|
||||||
background: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.983 12.522c-.21 0-.4-.07-.557-.226l-3.46-3.461a.777.777 0 0 1 0-1.113.777.777 0 0 1 1.112 0L6 10.626l6.94-6.922a.777.777 0 0 1 1.112 0c.313.313.313.8 0 1.113l-7.495 7.479a.83.83 0 0 1-.574.226z' fill='currentColor'/%3E%3C/svg%3E")
|
isRefined &&
|
||||||
no-repeat center;
|
`background: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.983 12.522c-.21 0-.4-.07-.557-.226l-3.46-3.461a.777.777 0 0 1 0-1.113.777.777 0 0 1 1.112 0L6 10.626l6.94-6.922a.777.777 0 0 1 1.112 0c.313.313.313.8 0 1.113l-7.495 7.479a.83.83 0 0 1-.574.226z' fill='currentColor'/%3E%3C/svg%3E") no-repeat center;`};
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FilterLabel = styled.span`
|
const FilterLabel = styled.span`
|
||||||
font-size: 0.833rem;
|
|
||||||
color: ${colors.textDarkPrimary};
|
color: ${colors.textDarkPrimary};
|
||||||
|
font-size: 0.83rem;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,53 +1,26 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Filter } from 'ts/components/docs/sidebar/filter';
|
import { FiltersGroup } from 'ts/components/docs/sidebar/filters_group';
|
||||||
import { Heading } from 'ts/components/text';
|
|
||||||
|
|
||||||
import { colors } from 'ts/style/colors';
|
|
||||||
import { styled } from 'ts/style/theme';
|
import { styled } from 'ts/style/theme';
|
||||||
|
|
||||||
export interface IFiltersProps {
|
interface IFiltersProps {
|
||||||
groups: FilterGroup[];
|
filters: IFiltersGroupProps[];
|
||||||
}
|
}
|
||||||
|
interface IFiltersGroupProps {
|
||||||
export interface FilterGroup {
|
attribute: string;
|
||||||
heading: string;
|
heading: string;
|
||||||
name: string;
|
|
||||||
filters: IFilterProps[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFilterProps {
|
export const Filters: React.FC<IFiltersProps> = ({ filters }) => (
|
||||||
value: string;
|
<FiltersWrapper>
|
||||||
label: string;
|
{filters.map((filter: IFiltersGroupProps, index: number) => (
|
||||||
}
|
<FiltersGroup key={`filter-${index}`} {...filter} />
|
||||||
|
))}
|
||||||
export const Filters: React.FC<IFiltersProps> = ({ groups }) => {
|
</FiltersWrapper>
|
||||||
return (
|
);
|
||||||
<FiltersWrapper>
|
|
||||||
{groups.map(({ heading, name, filters }: FilterGroup, groupIndex) => (
|
|
||||||
<FiltersGroupWrapper key={`filter-group-${groupIndex}`}>
|
|
||||||
<FilterGroupHeading asElement="h3">{heading}</FilterGroupHeading>
|
|
||||||
{filters.map(({ value, label }: IFilterProps, filterIndex) => (
|
|
||||||
<Filter key={`filter-${name}-${filterIndex}`} name={name} value={value} label={label} />
|
|
||||||
))}
|
|
||||||
</FiltersGroupWrapper>
|
|
||||||
))}
|
|
||||||
</FiltersWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const FiltersWrapper = styled.aside`
|
const FiltersWrapper = styled.aside`
|
||||||
position: relative;
|
position: relative;
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FiltersGroupWrapper = styled.div`
|
|
||||||
margin-bottom: 2.22em;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const FilterGroupHeading = styled(Heading)`
|
|
||||||
color: ${colors.textDarkPrimary};
|
|
||||||
font-size: 1rem !important;
|
|
||||||
font-weight: 400 !important;
|
|
||||||
margin-bottom: 1em !important;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connectRefinementList } from 'react-instantsearch-dom';
|
||||||
|
|
||||||
|
import { Filter, IFilterProps } from 'ts/components/docs/sidebar/filter';
|
||||||
|
import { Heading } from 'ts/components/text';
|
||||||
|
|
||||||
|
import { styled } from 'ts/style/theme';
|
||||||
|
|
||||||
|
interface IFilterListProps {
|
||||||
|
heading: string;
|
||||||
|
items: IFilterProps[];
|
||||||
|
refine: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FiltersList: React.FC<IFilterListProps> = ({ items, heading, refine }) => (
|
||||||
|
<FiltersGroupWrapper>
|
||||||
|
<Heading asElement="h3" size={18} fontWeight="400" marginBottom="1rem">
|
||||||
|
{heading}
|
||||||
|
</Heading>
|
||||||
|
{items.map((item: IFilterProps, index: number) => (
|
||||||
|
<Filter key={`filter-${index}`} refine={refine} {...item} />
|
||||||
|
))}
|
||||||
|
</FiltersGroupWrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FiltersGroup = connectRefinementList(FiltersList);
|
||||||
|
|
||||||
|
const FiltersGroupWrapper = styled.div`
|
||||||
|
margin-bottom: 2.22em;
|
||||||
|
`;
|
||||||
@@ -3,7 +3,7 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import { Hero } from 'ts/components/docs/hero';
|
import { Hero } from 'ts/components/docs/hero';
|
||||||
import { Resource } from 'ts/components/docs/resource/resource';
|
import { Resource } from 'ts/components/docs/resource/resource';
|
||||||
import { FilterGroup, Filters } from 'ts/components/docs/sidebar/filters';
|
import { Filters } from 'ts/components/docs/sidebar/filters';
|
||||||
import { SiteWrap } from 'ts/components/docs/siteWrap';
|
import { SiteWrap } from 'ts/components/docs/siteWrap';
|
||||||
import { DocumentTitle } from 'ts/components/document_title';
|
import { DocumentTitle } from 'ts/components/document_title';
|
||||||
import { Section } from 'ts/components/newLayout';
|
import { Section } from 'ts/components/newLayout';
|
||||||
@@ -16,7 +16,7 @@ export const DocsGuides: React.FC = () => {
|
|||||||
<Hero title="Guides" />
|
<Hero title="Guides" />
|
||||||
<Section maxWidth={'1030px'} isPadded={false} padding="0 0">
|
<Section maxWidth={'1030px'} isPadded={false} padding="0 0">
|
||||||
<Columns>
|
<Columns>
|
||||||
<Filters groups={filterGroups} />
|
<Filters filters={filters} />
|
||||||
<article>
|
<article>
|
||||||
{resources.map((resource, index) => (
|
{resources.map((resource, index) => (
|
||||||
<Resource key={`resource-${index}`} {...resource} />
|
<Resource key={`resource-${index}`} {...resource} />
|
||||||
@@ -39,46 +39,14 @@ const Columns = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const filterGroups: FilterGroup[] = [
|
const filters = [
|
||||||
{
|
{
|
||||||
|
attribute: 'Topic',
|
||||||
heading: 'Topic',
|
heading: 'Topic',
|
||||||
name: 'topic',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
value: 'Mesh',
|
|
||||||
label: 'Mesh',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Testing',
|
|
||||||
label: 'Testing',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Coordinator Model',
|
|
||||||
label: 'Coordinator Model',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Protocol developer',
|
|
||||||
label: 'Protocol developer',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
attribute: 'Difficulty',
|
||||||
heading: 'Level',
|
heading: 'Level',
|
||||||
name: 'level',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
value: 'Beginner',
|
|
||||||
label: 'Beginner',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Intermediate',
|
|
||||||
label: 'Intermediate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Advanced',
|
|
||||||
label: 'Advanced',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { Notification } from 'ts/components/docs/notification';
|
|||||||
import { OrderedList } from 'ts/components/docs/ordered_list';
|
import { OrderedList } from 'ts/components/docs/ordered_list';
|
||||||
import { Resource } from 'ts/components/docs/resource/resource';
|
import { Resource } from 'ts/components/docs/resource/resource';
|
||||||
import { Separator } from 'ts/components/docs/separator';
|
import { Separator } from 'ts/components/docs/separator';
|
||||||
import { FilterGroup, Filters } from 'ts/components/docs/sidebar/filters';
|
import { Filters } from 'ts/components/docs/sidebar/filters';
|
||||||
import { SiteWrap } from 'ts/components/docs/siteWrap';
|
import { SiteWrap } from 'ts/components/docs/siteWrap';
|
||||||
import { IStepLinkConfig } from 'ts/components/docs/step_link';
|
import { IStepLinkConfig } from 'ts/components/docs/step_link';
|
||||||
import { StepLinks } from 'ts/components/docs/step_links';
|
import { StepLinks } from 'ts/components/docs/step_links';
|
||||||
@@ -32,7 +32,7 @@ export const DocsPageTemplate: React.FC = () => {
|
|||||||
<Hero title={`Page Template`} description="This a subheader for the page" />
|
<Hero title={`Page Template`} description="This a subheader for the page" />
|
||||||
<Section maxWidth="1150px" isPadded={false} overflow="visible">
|
<Section maxWidth="1150px" isPadded={false} overflow="visible">
|
||||||
<Columns>
|
<Columns>
|
||||||
<Filters groups={filterGroups} />
|
<Filters filters={filters} />
|
||||||
<Separator />
|
<Separator />
|
||||||
<ContentWrapper>
|
<ContentWrapper>
|
||||||
<LargeHeading>Large Heading</LargeHeading>
|
<LargeHeading>Large Heading</LargeHeading>
|
||||||
@@ -286,46 +286,14 @@ const usefulLinks: IStepLinkConfig[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const filterGroups: FilterGroup[] = [
|
const filters = [
|
||||||
{
|
{
|
||||||
|
attribute: 'Topic',
|
||||||
heading: 'Topic',
|
heading: 'Topic',
|
||||||
name: 'topic',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
value: 'Mesh',
|
|
||||||
label: 'Mesh',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Testing',
|
|
||||||
label: 'Testing',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Coordinator Model',
|
|
||||||
label: 'Coordinator Model',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Protocol developer',
|
|
||||||
label: 'Protocol developer',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
attribute: 'Difficulty',
|
||||||
heading: 'Level',
|
heading: 'Level',
|
||||||
name: 'level',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
value: 'Beginner',
|
|
||||||
label: 'Beginner',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Intermediate',
|
|
||||||
label: 'Intermediate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Advanced',
|
|
||||||
label: 'Advanced',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import styled from 'styled-components';
|
|||||||
import { FeatureLink } from 'ts/components/docs/feature_link';
|
import { FeatureLink } from 'ts/components/docs/feature_link';
|
||||||
import { Hero } from 'ts/components/docs/hero';
|
import { Hero } from 'ts/components/docs/hero';
|
||||||
import { Resource } from 'ts/components/docs/resource/resource';
|
import { Resource } from 'ts/components/docs/resource/resource';
|
||||||
import { FilterGroup, Filters } from 'ts/components/docs/sidebar/filters';
|
import { Filters } from 'ts/components/docs/sidebar/filters';
|
||||||
import { SiteWrap } from 'ts/components/docs/siteWrap';
|
import { SiteWrap } from 'ts/components/docs/siteWrap';
|
||||||
import { DocumentTitle } from 'ts/components/document_title';
|
import { DocumentTitle } from 'ts/components/document_title';
|
||||||
import { Section } from 'ts/components/newLayout';
|
import { Section } from 'ts/components/newLayout';
|
||||||
@@ -12,51 +12,63 @@ import { Heading } from 'ts/components/text';
|
|||||||
|
|
||||||
import { documentConstants } from 'ts/utils/document_meta_constants';
|
import { documentConstants } from 'ts/utils/document_meta_constants';
|
||||||
|
|
||||||
|
import { ClearRefinements, Hits, InstantSearch } from 'react-instantsearch-dom';
|
||||||
|
|
||||||
|
import algoliasearch from 'algoliasearch/lite';
|
||||||
|
|
||||||
|
const searchClient = algoliasearch('39X6WOJZKW', '6acba761a34d99781628c6178af1e16c');
|
||||||
|
|
||||||
export const DocsTools: React.FC = () => {
|
export const DocsTools: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<SiteWrap theme="light">
|
<SiteWrap theme="light">
|
||||||
<DocumentTitle {...documentConstants.DOCS} />
|
<DocumentTitle {...documentConstants.DOCS} />
|
||||||
<Hero title="Tools" />
|
<Hero title="Tools" />
|
||||||
<Section maxWidth="1030px" isPadded={false}>
|
<Section maxWidth="1030px" isPadded={false}>
|
||||||
<Columns>
|
<InstantSearch searchClient={searchClient} indexName="0x_tools_test">
|
||||||
<Filters groups={filterGroups} />
|
<ClearRefinements />
|
||||||
<article>
|
|
||||||
<FeaturedToolsWrapper>
|
|
||||||
<Heading asElement="h2" size="default">
|
|
||||||
Featured Tools
|
|
||||||
</Heading>
|
|
||||||
{featuredLinks.map((link, index) => (
|
|
||||||
<FeatureLink
|
|
||||||
key={`featuredLink-${index}`}
|
|
||||||
heading={link.heading}
|
|
||||||
description={link.description}
|
|
||||||
icon={link.icon}
|
|
||||||
url={link.url}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</FeaturedToolsWrapper>
|
|
||||||
|
|
||||||
<ResourcesWrapper>
|
<Columns>
|
||||||
<Heading asElement="h2" size="default">
|
<Filters filters={filters} />
|
||||||
Docker Images
|
<article>
|
||||||
</Heading>
|
<Hits />
|
||||||
|
|
||||||
{resources.map((resource, index) => (
|
<FeaturedToolsWrapper>
|
||||||
<Resource key={`resource-${index}`} {...resource} />
|
<Heading asElement="h2" size="default">
|
||||||
))}
|
Featured Tools
|
||||||
</ResourcesWrapper>
|
</Heading>
|
||||||
|
{featuredLinks.map((link, index) => (
|
||||||
|
<FeatureLink
|
||||||
|
key={`featuredLink-${index}`}
|
||||||
|
heading={link.heading}
|
||||||
|
description={link.description}
|
||||||
|
icon={link.icon}
|
||||||
|
url={link.url}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</FeaturedToolsWrapper>
|
||||||
|
|
||||||
<ResourcesWrapper>
|
<ResourcesWrapper>
|
||||||
<Heading asElement="h2" size="default">
|
<Heading asElement="h2" size="default">
|
||||||
TypeScript Libraries
|
Docker Images
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
{resources.map((resource, index) => (
|
{resources.map((resource, index) => (
|
||||||
<Resource key={`resource-${index}`} {...resource} />
|
<Resource key={`resource-${index}`} {...resource} />
|
||||||
))}
|
))}
|
||||||
</ResourcesWrapper>
|
</ResourcesWrapper>
|
||||||
</article>
|
|
||||||
</Columns>
|
<ResourcesWrapper>
|
||||||
|
<Heading asElement="h2" size="default">
|
||||||
|
TypeScript Libraries
|
||||||
|
</Heading>
|
||||||
|
|
||||||
|
{resources.map((resource, index) => (
|
||||||
|
<Resource key={`resource-${index}`} {...resource} />
|
||||||
|
))}
|
||||||
|
</ResourcesWrapper>
|
||||||
|
</article>
|
||||||
|
</Columns>
|
||||||
|
</InstantSearch>
|
||||||
</Section>
|
</Section>
|
||||||
</SiteWrap>
|
</SiteWrap>
|
||||||
);
|
);
|
||||||
@@ -81,87 +93,9 @@ const ResourcesWrapper = styled.div`
|
|||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const filterGroups: FilterGroup[] = [
|
const filters = [
|
||||||
{
|
{ attribute: 'tags', heading: 'Developer persona' },
|
||||||
heading: 'Type',
|
{ attribute: 'difficulty', heading: 'Difficulty' },
|
||||||
name: 'type',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
value: 'Docker images',
|
|
||||||
label: 'Docker images',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Typescript/Javascript Libraries',
|
|
||||||
label: 'Typescript/Javascript Libraries',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Python Libraries',
|
|
||||||
label: 'Python Libraries',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Golang Libraries',
|
|
||||||
label: 'Golang Libraries',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Starter projects',
|
|
||||||
label: 'Starter projects',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Command-line tools',
|
|
||||||
label: 'Command-line tools',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: 'Developer Persona',
|
|
||||||
name: 'developerPersona',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
value: 'Relayer',
|
|
||||||
label: 'Relayer',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Trader',
|
|
||||||
label: 'Trader',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Instant integrator',
|
|
||||||
label: 'Instant integrator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Protocol developer',
|
|
||||||
label: 'Protocol developer',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: 'Level',
|
|
||||||
name: 'level',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
value: 'Beginner',
|
|
||||||
label: 'Beginner',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Intermediate',
|
|
||||||
label: 'Intermediate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Advanced',
|
|
||||||
label: 'Advanced',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: 'Communtity Maintained',
|
|
||||||
name: 'communityMaintained',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
value: '1',
|
|
||||||
label: 'Include Community Maintained',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const featuredLinks = [
|
const featuredLinks = [
|
||||||
|
|||||||
Reference in New Issue
Block a user