mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
language filter, url queries, and reset filters button
This commit is contained in:
1 parent
2acdf3253e
commit
e7c33abd72
2 files changed
+155
-36
No files matched your search
@@ -3,6 +3,9 @@ import { Weight } from "./GetWeight";
|
||||
|
||||
export interface SearchWeightsRequest {
|
||||
search_term: string;
|
||||
weight_type?: string;
|
||||
weight_category?: string;
|
||||
ietf_language_tag?: string;
|
||||
}
|
||||
|
||||
export interface SearchWeightsResponse {
|
||||
|
||||
@@ -4,22 +4,20 @@ import PageHeader from "components/layout/PageHeader";
|
||||
import Panel from "components/common/Panel";
|
||||
import { SearchWeights } from "@storyteller/components/src/api/weights/SearchWeights";
|
||||
import { Weight } from "@storyteller/components/src/api/weights/GetWeight";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import debounce from "lodash.debounce";
|
||||
import MasonryGrid from "components/common/MasonryGrid/MasonryGrid";
|
||||
import WeightsCards from "components/common/Card/WeightsCards";
|
||||
import { useBookmarks, useRatings } from "hooks";
|
||||
import { faFilter, faLanguage, faTag } from "@fortawesome/pro-solid-svg-icons";
|
||||
import {
|
||||
faFilter,
|
||||
faLanguage,
|
||||
faTag,
|
||||
faXmark,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import Select from "components/common/Select";
|
||||
|
||||
// const allTags = [
|
||||
// "English",
|
||||
// "Spanish",
|
||||
// "Portuguese",
|
||||
// "High-pitched",
|
||||
// "Low-pitched",
|
||||
// "Character",
|
||||
// ];
|
||||
import { AVAILABLE_TTS_LANGUAGE_CATEGORY_MAP } from "_i18n/AvailableLanguageMap";
|
||||
import { Button } from "components/common";
|
||||
|
||||
export default function SearchPage() {
|
||||
const [foundWeights, setFoundWeights] = useState<Weight[]>([]);
|
||||
@@ -29,9 +27,12 @@ export default function SearchPage() {
|
||||
const [weightTypeOpts, setWeightTypeOpts] = useState([
|
||||
{ value: "all", label: "All Types" },
|
||||
]);
|
||||
const [language, setLanguage] = useState<string>("all");
|
||||
|
||||
const bookmarks = useBookmarks();
|
||||
const ratings = useRatings();
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
||||
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
@@ -39,20 +40,28 @@ export default function SearchPage() {
|
||||
return new URLSearchParams(useLocation().search);
|
||||
};
|
||||
|
||||
const query = useQuery();
|
||||
const urlSearchTerm = query.get("query") || "";
|
||||
const urlWeightType = query.get("type") || "all";
|
||||
const urlWeightCategory = query.get("category") || "all";
|
||||
const urlLanguage = query.get("language") || "all";
|
||||
|
||||
const doSearch = useCallback(
|
||||
async (value: string) => {
|
||||
let request: any = {
|
||||
search_term: value,
|
||||
async (
|
||||
searchTerm,
|
||||
weightTypeFilter,
|
||||
weightCategoryFilter,
|
||||
languageFilter
|
||||
) => {
|
||||
let request = {
|
||||
search_term: searchTerm,
|
||||
weight_type: weightTypeFilter !== "all" ? weightTypeFilter : undefined,
|
||||
weight_category:
|
||||
weightCategoryFilter !== "all" ? weightCategoryFilter : undefined,
|
||||
ietf_language_subtag:
|
||||
languageFilter !== "all" ? languageFilter : undefined,
|
||||
};
|
||||
|
||||
if (weightType !== "all") {
|
||||
request["weight_type"] = weightType;
|
||||
}
|
||||
|
||||
if (weightCategory !== "all") {
|
||||
request["weight_category"] = weightCategory;
|
||||
}
|
||||
|
||||
let response = await SearchWeights(request);
|
||||
|
||||
if (response.success) {
|
||||
@@ -63,31 +72,68 @@ export default function SearchPage() {
|
||||
setFoundWeights([]);
|
||||
}
|
||||
},
|
||||
[setFoundWeights, weightType, weightCategory]
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[setFoundWeights, weightType, weightCategory, language]
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const debouncedDoSearch = useCallback(
|
||||
debounce(searchTerm => {
|
||||
doSearch(searchTerm);
|
||||
}, 250),
|
||||
debounce(
|
||||
(searchTerm, weightTypeFilter, weightCategoryFilter, languageFilter) => {
|
||||
doSearch(
|
||||
searchTerm,
|
||||
weightTypeFilter,
|
||||
weightCategoryFilter,
|
||||
languageFilter
|
||||
);
|
||||
},
|
||||
250
|
||||
),
|
||||
[doSearch]
|
||||
);
|
||||
|
||||
const query = useQuery();
|
||||
const urlSearchTerm = query.get("query") || "";
|
||||
|
||||
useEffect(() => {
|
||||
if (urlSearchTerm) {
|
||||
debouncedDoSearch(urlSearchTerm);
|
||||
debouncedDoSearch(
|
||||
urlSearchTerm,
|
||||
urlWeightType,
|
||||
urlWeightCategory,
|
||||
urlLanguage
|
||||
);
|
||||
}
|
||||
}, [urlSearchTerm, debouncedDoSearch]);
|
||||
}, [
|
||||
urlSearchTerm,
|
||||
urlWeightType,
|
||||
urlWeightCategory,
|
||||
urlLanguage,
|
||||
debouncedDoSearch,
|
||||
]);
|
||||
|
||||
const languageOpts = [
|
||||
{ value: "all", label: "All Languages" },
|
||||
{ value: "english", label: "English" },
|
||||
{ value: "spanish", label: "Spanish" },
|
||||
{ value: "portuguese", label: "Portuguese" },
|
||||
let languageOpts = Object.entries(AVAILABLE_TTS_LANGUAGE_CATEGORY_MAP).map(
|
||||
([languageCode, language]) => {
|
||||
let label = `${language.languageName}`;
|
||||
|
||||
if (language.languageNameLocalized !== undefined) {
|
||||
label = `${language.languageNameLocalized} / ${label}`;
|
||||
}
|
||||
|
||||
if (language.flags.length > 0) {
|
||||
label += ` ${language.flags.join(" ")}`;
|
||||
}
|
||||
|
||||
return {
|
||||
value: languageCode,
|
||||
label: label,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
languageOpts = [
|
||||
{
|
||||
label: `All Languages`,
|
||||
value: "all",
|
||||
},
|
||||
...languageOpts,
|
||||
];
|
||||
|
||||
const weightCategoryOpts = [
|
||||
@@ -149,6 +195,63 @@ export default function SearchPage() {
|
||||
weightCategoryOpts.find(el => el.value === weightCategory) ||
|
||||
weightCategoryOpts[0];
|
||||
|
||||
const languageValue =
|
||||
languageOpts.find(el => el.value === language) || languageOpts[0];
|
||||
|
||||
// update URL query parameters
|
||||
const updateQueryParams = (params: any) => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key] === "all") {
|
||||
searchParams.delete(key);
|
||||
} else {
|
||||
searchParams.set(key, params[key]);
|
||||
}
|
||||
});
|
||||
history.push({
|
||||
pathname: location.pathname,
|
||||
search: searchParams.toString(),
|
||||
});
|
||||
};
|
||||
|
||||
// Set initial filter states from URL query parameters
|
||||
useEffect(() => {
|
||||
setWeightType(query.get("type") || "all");
|
||||
setWeightCategory(query.get("category") || "all");
|
||||
setLanguage(query.get("language") || "all");
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Update URL when filters change
|
||||
useEffect(() => {
|
||||
updateQueryParams({
|
||||
query: urlSearchTerm,
|
||||
type: weightType,
|
||||
category: weightCategory,
|
||||
language: language,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [weightType, weightCategory, language, urlSearchTerm]);
|
||||
|
||||
const isFilterApplied =
|
||||
weightType !== "all" || weightCategory !== "all" || language !== "all";
|
||||
|
||||
const handleClearFilters = () => {
|
||||
setWeightType("all");
|
||||
setWeightCategory("all");
|
||||
setLanguage("all");
|
||||
|
||||
const updatedSearchParams = new URLSearchParams();
|
||||
if (urlSearchTerm !== "") updatedSearchParams.set("query", urlSearchTerm);
|
||||
|
||||
history.push({
|
||||
pathname: location.pathname,
|
||||
search: updatedSearchParams.toString(),
|
||||
});
|
||||
|
||||
doSearch(urlSearchTerm, "all", "all", "all");
|
||||
};
|
||||
|
||||
return (
|
||||
<Container type="panel" className="mb-5">
|
||||
<PageHeader
|
||||
@@ -164,7 +267,11 @@ export default function SearchPage() {
|
||||
icon: faLanguage,
|
||||
options: languageOpts,
|
||||
name: "languages",
|
||||
value: languageValue,
|
||||
defaultValue: languageOpts[0],
|
||||
onChange: args => {
|
||||
setLanguage(args.value);
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
@@ -191,6 +298,15 @@ export default function SearchPage() {
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{isFilterApplied && (
|
||||
<Button
|
||||
variant="link"
|
||||
icon={faXmark}
|
||||
label="Reset Filters"
|
||||
className="ms-2"
|
||||
onClick={handleClearFilters}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<MasonryGrid
|
||||
|
||||
Reference in new issue
Block a user