Skip to content

Commit 207cf3d

Browse files
m-hulbertmschristensen
authored andcommitted
Add a guides section into the navigation
1 parent 4629104 commit 207cf3d

File tree

12 files changed

+710
-2
lines changed

12 files changed

+710
-2
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import Icon from '@ably/ui/core/Icon';
3+
import cn from '@ably/ui/core/utils/cn';
4+
5+
const GuidesCheckbox = ({
6+
label,
7+
name,
8+
value,
9+
disabled = false,
10+
isChecked = false,
11+
handleSelect,
12+
indented = false,
13+
}: {
14+
label: string;
15+
name: string;
16+
value: string;
17+
disabled?: boolean;
18+
isChecked?: boolean;
19+
handleSelect: (e: React.ChangeEvent<HTMLInputElement>) => void;
20+
indented?: boolean;
21+
}) => {
22+
return (
23+
<div className={cn('flex items-center mb-0', indented && 'ml-4')}>
24+
<input
25+
data-ui-checkbox-native=""
26+
type="checkbox"
27+
id={name}
28+
data-testid={name}
29+
name={name}
30+
className="ui-checkbox-input"
31+
value={value}
32+
checked={isChecked}
33+
disabled={disabled}
34+
onChange={(e) => handleSelect(e)}
35+
/>
36+
<div
37+
data-ui-checkbox-styled=""
38+
className={cn(['ui-checkbox-styled', disabled && '!border-neutral-800 !bg-orange-600'])}
39+
>
40+
<Icon
41+
size="1rem"
42+
name="icon-gui-check-outline"
43+
additionalCSS={cn(['ui-checkbox-styled-tick cursor-pointer', disabled && 'text-neutral-000'])}
44+
/>
45+
</div>
46+
<label htmlFor={name} className="ui-text-p4 text-neutral-900">
47+
{label}
48+
</label>
49+
</div>
50+
);
51+
};
52+
53+
export default GuidesCheckbox;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import React, { ChangeEvent, useCallback, useEffect, useState } from 'react';
2+
import { StaticImage } from 'gatsby-plugin-image';
3+
import GuidesGrid from './GuidesGrid';
4+
import GuidesFilter from './GuidesFilter';
5+
import { ImageProps } from '../Image';
6+
import { guides } from '../../data/guides';
7+
import { filterSearchGuides, SelectedFilters } from './filter-search-guides';
8+
import GuidesNoResults from './GuidesNoResults';
9+
import { useLocation } from '@reach/router';
10+
import { GuideProduct, AIProvider } from '../../data/guides/types';
11+
import { products } from '../../data/guides';
12+
13+
const GuidesContent = ({ guideImages }: { guideImages: ImageProps[] }) => {
14+
const location = useLocation();
15+
16+
// Parse product and provider query parameters
17+
const getInitialFilters = (): SelectedFilters => {
18+
const params = new URLSearchParams(location.search);
19+
const productParam = params.get('product');
20+
const providerParam = params.get('provider');
21+
const validProductNames = Object.keys(products);
22+
23+
const initialProducts: GuideProduct[] = productParam
24+
? productParam
25+
.split(',')
26+
.map((p) => p.trim())
27+
.filter((product): product is GuideProduct => validProductNames.includes(product))
28+
: [];
29+
30+
const validProviderNames: AIProvider[] = ['anthropic', 'openai'];
31+
const initialProviders: AIProvider[] = providerParam
32+
? providerParam
33+
.split(',')
34+
.map((p) => p.trim())
35+
.filter((provider): provider is AIProvider => validProviderNames.includes(provider as AIProvider))
36+
: [];
37+
38+
return {
39+
products: initialProducts,
40+
aiProviders: initialProviders,
41+
};
42+
};
43+
44+
const [selected, setSelected] = useState<SelectedFilters>(getInitialFilters);
45+
const [searchTerm, setSearchTerm] = useState('');
46+
const [filteredGuides, setFilteredGuides] = useState(guides);
47+
48+
const handleSearch = useCallback((e: ChangeEvent<HTMLInputElement>) => {
49+
setSearchTerm(e.target.value);
50+
}, []);
51+
52+
useEffect(() => {
53+
const filtered = filterSearchGuides(guides, selected, searchTerm);
54+
setFilteredGuides(filtered);
55+
}, [selected, searchTerm]);
56+
57+
return (
58+
<>
59+
<section className="mx-auto px-6 md:px-0 relative">
60+
<div className="w-full sm:w-1/2 max-w-[37.5rem] pt-20 sm:pt-24">
61+
<h1 className="ui-text-title text-title">Guides</h1>
62+
<p className="ui-text-sub-header mt-4">
63+
In-depth guides to help you build realtime features with Ably and understand how best to architect an app
64+
for your use case.
65+
</p>
66+
</div>
67+
<div className="w-full my-10 sm:my-16 flex flex-col sm:flex-row gap-x-10">
68+
<div className="w-full sm:w-[20%] relative">
69+
<GuidesFilter selected={selected} setSelected={setSelected} handleSearch={handleSearch} />
70+
</div>
71+
<div className="w-full sm:w-[80%] mt-10 sm:mt-0">
72+
{filteredGuides.length > 0 ? (
73+
<GuidesGrid guideImages={guideImages} guides={filteredGuides} searchTerm={searchTerm} />
74+
) : (
75+
<GuidesNoResults />
76+
)}
77+
</div>
78+
</div>
79+
</section>
80+
81+
<StaticImage
82+
src="../Examples/images/pattern-grid.png"
83+
placeholder="blurred"
84+
width={660}
85+
height={282}
86+
alt="Grid Pattern"
87+
className="!absolute -z-10 right-0 top-16 !hidden sm:!block w-[60%] md:w-[40%]"
88+
/>
89+
90+
<StaticImage
91+
src="../Examples/images/mobile-grid.png"
92+
placeholder="blurred"
93+
width={260}
94+
alt="Grid Pattern"
95+
className="-z-10 right-0 top-16 !absolute !block sm:!hidden"
96+
/>
97+
</>
98+
);
99+
};
100+
101+
export default GuidesContent;

0 commit comments

Comments
 (0)