Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions client/src/app/features/search/apis/search-api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {
ApiWorldHeritageDto,
CriteriaCode,
ListResult,
Pagination,
StudyRegion,
Expand All @@ -16,6 +17,8 @@ export type SearchParams = {
category?: string;
yearInscribedFrom?: number;
yearInscribedTo?: number;
isEndangered?: boolean;
criteria?: readonly CriteriaCode[];
currentPage?: number;
perPage?: number;
};
Expand Down Expand Up @@ -72,6 +75,10 @@ export const createSearchApi = ({ apiBase, fetchImpl = fetch }: SearchApiDeps) =
if (category) queryParams.set("category", category);
if (yearInscribedFrom) queryParams.set("year_inscribed_from", yearInscribedFrom);
if (yearInscribedTo) queryParams.set("year_inscribed_to", yearInscribedTo);
if (params.isEndangered === true) queryParams.set("is_endangered", "true");
if (params.criteria && params.criteria.length > 0) {
queryParams.set("criteria", params.criteria.join(","));
}
if (params.currentPage != null) queryParams.set("current_page", String(params.currentPage));
if (params.perPage != null) queryParams.set("per_page", String(params.perPage));

Expand Down
10 changes: 7 additions & 3 deletions client/src/app/features/search/components/SearchResultsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { HeritageCard } from "@features/top/cards/HeritageCard";
import { Pagination } from "@features/top/components/Pagination.tsx";
import { BreadcrumbList } from "@shared/components/BreadcrumbList.tsx";
import { SearchResultMapComponent } from "@features/search/components/SearchResultMapComponent.tsx";
import { LocaleToggle } from "@shared/locale/LocaleToggle.tsx";
import { useText } from "@shared/locale/ui-text.ts";

type Props = {
header?: ReactNode;
Expand All @@ -31,6 +33,7 @@ export default function SearchResultsPage({
onPageChange,
onBackToAllSites,
}: Props) {
const text = useText();
return (
<main className="mx-auto max-w-7xl px-4 py-12">
<div className="sticky top-0 z-20 -mx-4 border-b border-zinc-200 bg-white/95 px-4 py-3 backdrop-blur">
Expand Down Expand Up @@ -69,11 +72,12 @@ export default function SearchResultsPage({
h-9 rounded-xl border border-zinc-200 bg-white px-3 text-xs font-semibold text-zinc-700
shadow-sm transition hover:bg-zinc-50
"
aria-label="Back to all sites"
aria-label={text.backToAllSites}
>
Back to all sites
{text.backToAllSites}
</button>
) : null}
<LocaleToggle />
</div>
</div>
</div>
Expand All @@ -87,7 +91,7 @@ export default function SearchResultsPage({

{items.length === 0 ? (
<div className="py-20 text-center">
<p className="text-sm text-zinc-600">No sites found.</p>
<p className="text-sm text-zinc-600">{text.noSitesFound}</p>
</div>
) : (
<ul className="grid list-none grid-cols-1 gap-6 p-0 md:grid-cols-2 lg:grid-cols-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ type SearchValues = {
keyword: string;
yearInscribedFrom: string;
yearInscribedTo: string;
isEndangered: boolean;
criteria: string[];
};

type HeritageSubHeaderProps = {
Expand Down Expand Up @@ -144,6 +146,8 @@ const makeParsedParams = (overrides: Partial<HeritageSearchParams> = {}): Herita
category: null,
year_inscribed_from: null,
year_inscribed_to: null,
is_endangered: null,
criteria: [],
current_page: 1,
per_page: 30,
order: "asc",
Expand Down Expand Up @@ -203,6 +207,8 @@ describe("TopPageContainer", () => {
keyword: "Kyoto",
yearInscribedFrom: "",
yearInscribedTo: "",
isEndangered: false,
criteria: [],
});
});

Expand Down Expand Up @@ -252,6 +258,8 @@ describe("TopPageContainer", () => {
category: "Cultural",
year_inscribed_from: 1990,
year_inscribed_to: null,
is_endangered: null,
criteria: [],
current_page: 1,
per_page: 30,
order: "asc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const toSearchValues = (params: HeritageSearchParams): SearchValues => ({
keyword: params.search_query ?? "",
yearInscribedFrom: params.year_inscribed_from !== null ? String(params.year_inscribed_from) : "",
yearInscribedTo: params.year_inscribed_to !== null ? String(params.year_inscribed_to) : "",
isEndangered: params.is_endangered === true,
criteria: params.criteria,
});

export function SearchHeritageFormContainer() {
Expand Down Expand Up @@ -71,6 +73,8 @@ export function SearchHeritageFormContainer() {
keyword: query.keyword ?? draft.keyword,
yearInscribedFrom: query.yearInscribedFrom ?? draft.yearInscribedFrom,
yearInscribedTo: query.yearInscribedTo ?? draft.yearInscribedTo,
isEndangered: query.isEndangered ?? draft.isEndangered,
criteria: query.criteria ?? draft.criteria,
};

const nextParams: HeritageSearchParams = {
Expand All @@ -80,17 +84,27 @@ export function SearchHeritageFormContainer() {
category: toCategoryOrNull(merged.category),
year_inscribed_from: toSearchYearOrNull(merged.yearInscribedFrom),
year_inscribed_to: toSearchYearOrNull(merged.yearInscribedTo),
is_endangered: merged.isEndangered ? true : null,
criteria: merged.criteria,
current_page: 1,
per_page: params.per_page ?? DEFAULT_TOP_PER_PAGE,
order: params.order ?? DEFAULT_ORDER,
country: null,
};

const search = serializeHeritageSearchParams(nextParams);
const baseSearch = serializeHeritageSearchParams(nextParams);
const currentLang = new URLSearchParams(location.search).get("lang");
const finalParams = new URLSearchParams(
baseSearch.startsWith("?") ? baseSearch.slice(1) : baseSearch,
);
if (currentLang === "ja") finalParams.set("lang", "ja");
const finalQueryString = finalParams.toString();
const search = finalQueryString ? `?${finalQueryString}` : "";

navigate({ pathname: "/heritages/results", search }, { replace: false });
setDraft(merged);
},
[draft, navigate, params.per_page, params.order],
[draft, navigate, params.per_page, params.order, location.search],
);

return <HeritageSubHeader value={draft} onChange={handleChange} onSubmit={handleSubmit} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,18 @@ const hasSearchParams = (params: HeritageSearchParams): boolean =>
params.region !== null ||
params.category !== null ||
params.year_inscribed_from !== null ||
params.year_inscribed_to !== null;
params.year_inscribed_to !== null ||
params.is_endangered === true ||
params.criteria.length > 0;

const toDraftValues = (params: HeritageSearchParams): SearchValues => ({
region: params.region ?? "",
category: params.category ?? "",
keyword: params.search_query ?? "",
yearInscribedFrom: params.year_inscribed_from !== null ? String(params.year_inscribed_from) : "",
yearInscribedTo: params.year_inscribed_to !== null ? String(params.year_inscribed_to) : "",
isEndangered: params.is_endangered === true,
criteria: params.criteria,
});

const toSearchParams = (draft: SearchValues): HeritageSearchParams => ({
Expand All @@ -67,6 +71,8 @@ const toSearchParams = (draft: SearchValues): HeritageSearchParams => ({
category: draft.category || null,
year_inscribed_from: draft.yearInscribedFrom ? Number(draft.yearInscribedFrom) : null,
year_inscribed_to: draft.yearInscribedTo ? Number(draft.yearInscribedTo) : null,
is_endangered: draft.isEndangered ? true : null,
criteria: draft.criteria,
current_page: 1,
});

Expand All @@ -76,8 +82,19 @@ const mergeDraft = (currentDraft: SearchValues, partial: Partial<SearchValues>):
keyword: partial.keyword ?? currentDraft.keyword,
yearInscribedFrom: partial.yearInscribedFrom ?? currentDraft.yearInscribedFrom,
yearInscribedTo: partial.yearInscribedTo ?? currentDraft.yearInscribedTo,
isEndangered: partial.isEndangered ?? currentDraft.isEndangered,
criteria: partial.criteria ?? currentDraft.criteria,
});

// 現在の URL に lang=ja があれば、遷移先 search にも持たせる (en はデフォルトなので付けない)
const preserveLang = (nextSearch: string, currentSearch: string): string => {
const currentLang = new URLSearchParams(currentSearch).get("lang");
if (currentLang !== "ja") return nextSearch;
const params = new URLSearchParams(nextSearch.startsWith("?") ? nextSearch.slice(1) : nextSearch);
params.set("lang", "ja");
return `?${params.toString()}`;
};

function useHeritageSearchDraft(params: HeritageSearchParams) {
const [draft, setDraft] = React.useState<SearchValues>(() => toDraftValues(params));

Expand Down Expand Up @@ -121,9 +138,10 @@ export function SearchHeritageResultsContainer(): React.ReactElement {

const handleClickItem = React.useCallback(
(id: number) => {
navigate(`/heritages/${id}`);
const search = preserveLang("", location.search);
navigate(`/heritages/${id}${search}`);
},
[navigate],
[navigate, location.search],
);

const handlePageChange = React.useCallback(
Expand All @@ -133,7 +151,7 @@ export function SearchHeritageResultsContainer(): React.ReactElement {
current_page: page,
};

const search = serializeHeritageSearchParams(nextParams);
const search = preserveLang(serializeHeritageSearchParams(nextParams), location.search);

navigate(
{
Expand All @@ -143,14 +161,14 @@ export function SearchHeritageResultsContainer(): React.ReactElement {
{ replace: false },
);
},
[navigate, location.pathname, params],
[navigate, location.pathname, location.search, params],
);

const handleSubmit = React.useCallback(
(partial: Partial<SearchValues>) => {
const nextDraft = mergeDraft(draft, partial);
const nextParams = toSearchParams(nextDraft);
const search = serializeHeritageSearchParams(nextParams);
const search = preserveLang(serializeHeritageSearchParams(nextParams), location.search);

navigate(
{
Expand All @@ -162,13 +180,14 @@ export function SearchHeritageResultsContainer(): React.ReactElement {

setDraft(nextDraft);
},
[draft, navigate, setDraft],
[draft, navigate, setDraft, location.search],
);

// Hooks must be called at the top level before any early returns.
const handleBackToAllSites = React.useCallback(() => {
navigate("/heritages", { replace: true });
}, [navigate]);
const search = preserveLang("", location.search);
navigate(`/heritages${search}`, { replace: true });
}, [navigate, location.search]);

const header = (
<HeritageSubHeader value={draft} onChange={handleChange} onSubmit={handleSubmit} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ const makeParams = (overrides: Partial<HeritageSearchParams> = {}): HeritageSear
category: null,
year_inscribed_from: null,
year_inscribed_to: null,
is_endangered: null,
criteria: [],
current_page: 1,
per_page: 30,
order: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const toSearchParams = (params: HeritageSearchParams): SearchParams => ({
category: params.category ?? undefined,
yearInscribedFrom: params.year_inscribed_from ?? undefined,
yearInscribedTo: params.year_inscribed_to ?? undefined,
isEndangered: params.is_endangered === true ? true : undefined,
criteria: params.criteria.length > 0 ? params.criteria : undefined,
currentPage: params.current_page,
perPage: params.per_page,
});
Expand Down
Loading
Loading