Skip to content

Commit 5789211

Browse files
committed
add searchbar functionality
1 parent 198114b commit 5789211

File tree

11 files changed

+519
-56
lines changed

11 files changed

+519
-56
lines changed

@types/generated/contentful.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,14 @@ export interface ICoachFields {
193193
/** Die Coaches */
194194

195195
export interface ICoach extends Entry<ICoachFields> {
196+
fields: ICoachFields
196197
sys: {
197198
id: string;
198199
type: string;
199200
createdAt: string;
200201
updatedAt: string;
201202
locale: string;
203+
202204
contentType: {
203205
sys: {
204206
id: "coach";

app/components/CoachSearch.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import SearchBar from "./SearchBar";
2+
3+
4+
function CoachSearch() {
5+
return (
6+
<div className="flex flex-row-reverse py-5 max-w-7xl mx-auto">
7+
<SearchBar />
8+
</div>
9+
);
10+
}
11+
12+
export default CoachSearch;

app/components/SearchBar.tsx

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,30 @@
11
import { Form } from "@remix-run/react";
2-
import React, { useState } from 'react';
2+
import React, { RefObject, useRef, useState } from 'react';
33
import SearchIcon from "./icons/SearchIcon";
44

5-
interface SearchBarProps {
6-
onSearch: (searchTerm: string) => void;
7-
}
85

9-
const SearchBar: React.FC<SearchBarProps> = ({ onSearch }) => {
6+
const SearchBar = () => {
107

118
const [searchTerm, setSearchTerm] = useState('');
12-
13-
const handleSearch = () => {
14-
onSearch(searchTerm);
15-
};
16-
179
return (
18-
<div className="inline-flex gap-2 mx-24">
19-
<Form>
10+
<div className="inline-flex gap-2">
2011
<input
12+
name="search"
2113
type="text"
2214
placeholder="Suche..."
2315
className="text-[1rem] px-2 py-1 rounded-full hover:text-vsp-900 border border-vsp-400 active:border-vsp-900 focus:border-vsp-900 disabled:border-vsp-200"
2416
value={searchTerm}
2517
onChange={(e) => setSearchTerm(e.target.value)}
2618
>
2719
</input>
28-
</Form>
29-
<button
30-
className="py-1 px-4 font-inherit inline-flex items-center justify-center rounded-full bg-vsp-400 text-vsp-900 hover:text-white no-underline transition-opacity duration-300 hover:bg-vsp-900 focus:opacity-90 active:opacity-90 disabled:pointer-events-none disabled:bg-vsp-200 md:text-lg"
31-
type="submit"
20+
{/* <button
21+
className="py-2 px-2 font-inherit inline-flex items-center justify-center rounded-full text-vsp-900 hover:text-white no-underline transition-opacity duration-300 hover:bg-vsp-900 focus:opacity-90 active:opacity-90 disabled:pointer-events-none "
22+
type="button"
3223
disabled={!searchTerm}
33-
onClick={handleSearch}
24+
onClick={()=>{setSearchTerm('')}}
3425
>
35-
<SearchIcon />
36-
</button>
26+
CLEAR
27+
</button> */}
3728
</div>
3829
);
3930
};

app/components/Searching.tsx

Lines changed: 0 additions & 18 deletions
This file was deleted.

app/components/SpeakingTimeContent.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import ContentBlocks from "./ContentBlocks";
1414
import ArrowDown from "./icons/ArrowDown";
1515
import FilterIcon from "./icons/FilterIcon";
1616
import Spinner from "./icons/Spinner";
17-
import Searching from "./Searching";
17+
import CoachSearch from "./CoachSearch";
1818
import CoachCard from "./CoachCard";
1919
import ContentfulRichText from "./ContentfulRichText";
2020
import type { EmailTemplate } from "~/utils/contentful";
@@ -53,6 +53,7 @@ export default function SpeakingTimeContent({
5353
const submit = useSubmit();
5454
const formRef = useRef<HTMLFormElement>(null);
5555
const { t } = useTranslation("searchingCoach");
56+
const [isActive, setIsActive] = useState(false);
5657

5758
const handleChange = () => {
5859
if (formRef) {
@@ -61,7 +62,7 @@ export default function SpeakingTimeContent({
6162
};
6263
const state = useNavigation();
6364

64-
const [isActive, setIsActive] = useState(false);
65+
6566
// sort tags to ensure the "quick response" one is always the first in the array
6667
tags.unshift(
6768
tags.splice(
@@ -77,8 +78,14 @@ export default function SpeakingTimeContent({
7778
return (
7879
<div>
7980
<ContentBlocks content={page.fields.content} locale={locale} />
80-
<Searching />
81-
<details open={true} className="mx-auto max-w-7xl py-8 px-4">
81+
<Form
82+
onChange={handleChange}
83+
ref={formRef}
84+
method="get"
85+
id="filter-form"
86+
className=""
87+
>
88+
<details open={false} className="mx-auto max-w-7xl py-8 px-4">
8289
<summary
8390
className="inline-flex cursor-pointer items-center hover:text-vsp-500"
8491
onClick={() => setIsActive(!isActive)}
@@ -96,13 +103,7 @@ export default function SpeakingTimeContent({
96103
</div>
97104
</summary>
98105

99-
<Form
100-
onChange={handleChange}
101-
ref={formRef}
102-
method="get"
103-
id="filter-form"
104-
className="flex flex-col gap-2"
105-
>
106+
106107
<fieldset className="mt-8">
107108
<legend className="mb-4 inline-block text-xl">
108109
{t("filter.language")}
@@ -180,11 +181,14 @@ export default function SpeakingTimeContent({
180181
</button>
181182
</noscript>
182183
</div>
183-
</Form>
184184
</details>
185+
<CoachSearch />
186+
</Form>
187+
185188
<div className="text-m mx-auto max-w-7xl py-4 px-4 font-semibold text-slate-700">
186189
{coachesAmount ? `${coachesAmount} ${t("result")}` : t("noResult")}
187190
</div>
191+
188192
<div className="relative">
189193
{state.state === "loading" && (
190194
<div className="absolute inset-0 z-50 flex items-start justify-center bg-white bg-opacity-50">

app/components/icons/CancelIcon.tsx

Whitespace-only changes.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { expect, test } from 'vitest'
2+
import { documentContentToSimpleString } from './documentToSimpleString'
3+
const exampleDescription = {
4+
"data": {},
5+
"content": [
6+
{
7+
"data": {},
8+
"content": [
9+
{
10+
"data": {},
11+
"content": [
12+
{
13+
"data": {},
14+
"content": [
15+
{
16+
"data": {},
17+
"marks": [],
18+
"value": "A",
19+
"nodeType": "text"
20+
}
21+
],
22+
"nodeType": "paragraph"
23+
}
24+
],
25+
"nodeType": "list-item"
26+
},
27+
{
28+
"data": {},
29+
"content": [
30+
{
31+
"data": {},
32+
"content": [
33+
{
34+
"data": {},
35+
"marks": [],
36+
"value": "B",
37+
"nodeType": "text"
38+
}
39+
],
40+
"nodeType": "paragraph"
41+
}
42+
],
43+
"nodeType": "list-item"
44+
},
45+
{
46+
"data": {},
47+
"content": [
48+
{
49+
"data": {},
50+
"content": [
51+
{
52+
"data": {},
53+
"marks": [],
54+
"value": "C",
55+
"nodeType": "text"
56+
}
57+
],
58+
"nodeType": "paragraph"
59+
}
60+
],
61+
"nodeType": "list-item"
62+
}
63+
],
64+
"nodeType": "unordered-list"
65+
},
66+
{
67+
"data": {},
68+
"content": [
69+
{
70+
"data": {},
71+
"marks": [],
72+
"value": "",
73+
"nodeType": "text"
74+
}
75+
],
76+
"nodeType": "paragraph"
77+
}
78+
],
79+
"nodeType": "document"
80+
}
81+
82+
test('get simple string from Document object', () => {
83+
const simpleText = documentContentToSimpleString(exampleDescription.content);
84+
expect(simpleText).toEqual("ABC");
85+
})
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// This function processes each individual node of the Rich Text document
2+
export function documentContentToSimpleString(nodes: any) {
3+
// Initialize an empty string to hold the result
4+
let result = '';
5+
6+
// Loop through each node in the input content array
7+
nodes.forEach((node :any) => {
8+
// If the node type is 'text', we append its value to the result
9+
if (node.nodeType === 'text' && node.value) {
10+
result += node.value;
11+
}
12+
13+
// If the node type is a list or list-item, recurse into its content
14+
else if (node.nodeType === 'unordered-list' || node.nodeType === 'list-item') {
15+
if (node.content) {
16+
// Recursively process the content of the list or list-item node
17+
result += documentContentToSimpleString(node.content);
18+
}
19+
}
20+
21+
// If the node type is paragraph, recurse into its content
22+
else if (node.nodeType === 'paragraph' && node.content) {
23+
result += documentContentToSimpleString(node.content);
24+
}
25+
});
26+
27+
// Return the concatenated string
28+
return result;
29+
}
30+

app/utils/getSearchPageContents.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from "~/utils/contentful";
1010
import pageIds from "~/utils/pageIds";
1111
import type { ICoachtag, LOCALE_CODE } from "../../@types/generated/contentful";
12+
import { documentContentToSimpleString } from "./documentToSimpleString";
1213

1314
export const getSearchPageContents = async (
1415
request: Request,
@@ -21,6 +22,9 @@ export const getSearchPageContents = async (
2122

2223
const checkedGender = searchParams.getAll("gender");
2324

25+
const searchTerm = searchParams.getAll("search");
26+
console.log(searchTerm);
27+
2428
const [page, coaches, languages, gender, tags, navigation, emailTemplate] =
2529
await Promise.all([
2630
getPageById(pageIds.SEARCH_HELP, locale),
@@ -40,6 +44,7 @@ export const getSearchPageContents = async (
4044
if (!navigation) {
4145
throw new Response("Could not load navigation", { status: 404 });
4246
}
47+
4348

4449
const filteredCoaches = coaches
4550
.filter((coach) => {
@@ -65,7 +70,15 @@ export const getSearchPageContents = async (
6570
!!coachGenders &&
6671
coachGenders.some((gender) => checkedGender.includes(gender))
6772
);
68-
});
73+
})
74+
.filter((coach) => {
75+
if(searchTerm[0] && searchTerm[0] != '') {
76+
const description = documentContentToSimpleString(coach.fields.description?.content);
77+
return `${coach.fields.name} ${description}`.includes(searchTerm[0])
78+
} else {
79+
return true
80+
}
81+
})
6982

7083
// get available tags from all coaches
7184

@@ -85,9 +98,8 @@ export const getSearchPageContents = async (
8598
checkedTags,
8699
checkedGender,
87100
locale,
88-
currentLang: lang,
89-
coachesAmount: filteredCoaches?.length || 0,
90-
availableTagIDs,
101+
currentLang: lang,
102+
coachesAmount: filteredCoaches?.length || 0, availableTagIDs,
91103
emailTemplate,
92104
};
93105
};

0 commit comments

Comments
 (0)