@stapel/search-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/MODULE.md +203 -0
- package/README.md +175 -0
- package/dist/api/generated/schema.d.ts +375 -0
- package/dist/api/generated/schema.d.ts.map +1 -0
- package/dist/api/generated/schema.js +2 -0
- package/dist/api/generated/schema.js.map +1 -0
- package/dist/api/searchApi.d.ts +77 -0
- package/dist/api/searchApi.d.ts.map +1 -0
- package/dist/api/searchApi.js +79 -0
- package/dist/api/searchApi.js.map +1 -0
- package/dist/api/types.d.ts +131 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +20 -0
- package/dist/api/types.js.map +1 -0
- package/dist/default/DegradationNotice.d.ts +19 -0
- package/dist/default/DegradationNotice.d.ts.map +1 -0
- package/dist/default/DegradationNotice.js +14 -0
- package/dist/default/DegradationNotice.js.map +1 -0
- package/dist/default/ErrorAlert.d.ts +9 -0
- package/dist/default/ErrorAlert.d.ts.map +1 -0
- package/dist/default/ErrorAlert.js +27 -0
- package/dist/default/ErrorAlert.js.map +1 -0
- package/dist/default/FacetPanelPane.d.ts +26 -0
- package/dist/default/FacetPanelPane.d.ts.map +1 -0
- package/dist/default/FacetPanelPane.js +32 -0
- package/dist/default/FacetPanelPane.js.map +1 -0
- package/dist/default/RankingDisclosurePane.d.ts +17 -0
- package/dist/default/RankingDisclosurePane.d.ts.map +1 -0
- package/dist/default/RankingDisclosurePane.js +23 -0
- package/dist/default/RankingDisclosurePane.js.map +1 -0
- package/dist/default/SearchPage.d.ts +34 -0
- package/dist/default/SearchPage.d.ts.map +1 -0
- package/dist/default/SearchPage.js +16 -0
- package/dist/default/SearchPage.js.map +1 -0
- package/dist/default/SearchResultCard.d.ts +34 -0
- package/dist/default/SearchResultCard.d.ts.map +1 -0
- package/dist/default/SearchResultCard.js +38 -0
- package/dist/default/SearchResultCard.js.map +1 -0
- package/dist/default/SearchResultsPane.d.ts +32 -0
- package/dist/default/SearchResultsPane.d.ts.map +1 -0
- package/dist/default/SearchResultsPane.js +46 -0
- package/dist/default/SearchResultsPane.js.map +1 -0
- package/dist/default/SortSelect.d.ts +22 -0
- package/dist/default/SortSelect.d.ts.map +1 -0
- package/dist/default/SortSelect.js +39 -0
- package/dist/default/SortSelect.js.map +1 -0
- package/dist/default/UrlIssueNotice.d.ts +12 -0
- package/dist/default/UrlIssueNotice.d.ts.map +1 -0
- package/dist/default/UrlIssueNotice.js +13 -0
- package/dist/default/UrlIssueNotice.js.map +1 -0
- package/dist/default/index.d.ts +40 -0
- package/dist/default/index.d.ts.map +1 -0
- package/dist/default/index.js +34 -0
- package/dist/default/index.js.map +1 -0
- package/dist/default/theme.d.ts +24 -0
- package/dist/default/theme.d.ts.map +1 -0
- package/dist/default/theme.js +39 -0
- package/dist/default/theme.js.map +1 -0
- package/dist/default/types.d.ts +12 -0
- package/dist/default/types.d.ts.map +1 -0
- package/dist/default/types.js +2 -0
- package/dist/default/types.js.map +1 -0
- package/dist/headless/FacetPanel.d.ts +61 -0
- package/dist/headless/FacetPanel.d.ts.map +1 -0
- package/dist/headless/FacetPanel.js +57 -0
- package/dist/headless/FacetPanel.js.map +1 -0
- package/dist/headless/RankingDisclosure.d.ts +39 -0
- package/dist/headless/RankingDisclosure.d.ts.map +1 -0
- package/dist/headless/RankingDisclosure.js +28 -0
- package/dist/headless/RankingDisclosure.js.map +1 -0
- package/dist/headless/SearchProvider.d.ts +19 -0
- package/dist/headless/SearchProvider.d.ts.map +1 -0
- package/dist/headless/SearchProvider.js +15 -0
- package/dist/headless/SearchProvider.js.map +1 -0
- package/dist/headless/SearchResults.d.ts +68 -0
- package/dist/headless/SearchResults.d.ts.map +1 -0
- package/dist/headless/SearchResults.js +70 -0
- package/dist/headless/SearchResults.js.map +1 -0
- package/dist/headless/SearchStateProvider.d.ts +69 -0
- package/dist/headless/SearchStateProvider.d.ts.map +1 -0
- package/dist/headless/SearchStateProvider.js +71 -0
- package/dist/headless/SearchStateProvider.js.map +1 -0
- package/dist/i18n/errorsMap.d.ts +27 -0
- package/dist/i18n/errorsMap.d.ts.map +1 -0
- package/dist/i18n/errorsMap.js +37 -0
- package/dist/i18n/errorsMap.js.map +1 -0
- package/dist/i18n/es.d.ts +13 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +85 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/generated/errors.es.gen.d.ts +9 -0
- package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.es.gen.js +63 -0
- package/dist/i18n/generated/errors.es.gen.js.map +1 -0
- package/dist/i18n/generated/errors.gen.d.ts +359 -0
- package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.gen.js +183 -0
- package/dist/i18n/generated/errors.gen.js.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts +9 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.js +63 -0
- package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
- package/dist/i18n/keys.d.ts +107 -0
- package/dist/i18n/keys.d.ts.map +1 -0
- package/dist/i18n/keys.js +185 -0
- package/dist/i18n/keys.js.map +1 -0
- package/dist/i18n/ru.d.ts +29 -0
- package/dist/i18n/ru.d.ts.map +1 -0
- package/dist/i18n/ru.js +101 -0
- package/dist/i18n/ru.js.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/model/context.d.ts +11 -0
- package/dist/model/context.d.ts.map +1 -0
- package/dist/model/context.js +16 -0
- package/dist/model/context.js.map +1 -0
- package/dist/model/queries.d.ts +48 -0
- package/dist/model/queries.d.ts.map +1 -0
- package/dist/model/queries.js +65 -0
- package/dist/model/queries.js.map +1 -0
- package/dist/model/queryKeys.d.ts +25 -0
- package/dist/model/queryKeys.d.ts.map +1 -0
- package/dist/model/queryKeys.js +22 -0
- package/dist/model/queryKeys.js.map +1 -0
- package/dist/model/runtime.d.ts +25 -0
- package/dist/model/runtime.d.ts.map +1 -0
- package/dist/model/runtime.js +6 -0
- package/dist/model/runtime.js.map +1 -0
- package/dist/nav/manifest.d.ts +21 -0
- package/dist/nav/manifest.d.ts.map +1 -0
- package/dist/nav/manifest.js +34 -0
- package/dist/nav/manifest.js.map +1 -0
- package/dist/router/index.d.ts +10 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +34 -0
- package/dist/router/index.js.map +1 -0
- package/dist/state/degradations.d.ts +30 -0
- package/dist/state/degradations.d.ts.map +1 -0
- package/dist/state/degradations.js +52 -0
- package/dist/state/degradations.js.map +1 -0
- package/dist/state/facets.d.ts +68 -0
- package/dist/state/facets.d.ts.map +1 -0
- package/dist/state/facets.js +171 -0
- package/dist/state/facets.js.map +1 -0
- package/dist/state/urlState.d.ts +143 -0
- package/dist/state/urlState.d.ts.map +1 -0
- package/dist/state/urlState.js +319 -0
- package/dist/state/urlState.js.map +1 -0
- package/llms.txt +87 -0
- package/manifest.json +652 -0
- package/nav-manifest.json +44 -0
- package/package.json +133 -0
- package/src/analytics/generated/events.json +7 -0
- package/src/api/generated/schema.ts +377 -0
- package/src/api/searchApi.ts +169 -0
- package/src/api/types.ts +160 -0
- package/src/default/DegradationNotice.tsx +50 -0
- package/src/default/ErrorAlert.tsx +46 -0
- package/src/default/FacetPanelPane.tsx +174 -0
- package/src/default/RankingDisclosurePane.tsx +116 -0
- package/src/default/SearchPage.tsx +87 -0
- package/src/default/SearchResultCard.tsx +96 -0
- package/src/default/SearchResultsPane.tsx +173 -0
- package/src/default/SortSelect.tsx +77 -0
- package/src/default/UrlIssueNotice.tsx +37 -0
- package/src/default/index.ts +48 -0
- package/src/default/theme.tsx +66 -0
- package/src/default/types.ts +12 -0
- package/src/headless/FacetPanel.tsx +113 -0
- package/src/headless/RankingDisclosure.tsx +58 -0
- package/src/headless/SearchProvider.tsx +20 -0
- package/src/headless/SearchResults.tsx +149 -0
- package/src/headless/SearchStateProvider.tsx +175 -0
- package/src/i18n/errorsMap.ts +50 -0
- package/src/i18n/es.ts +110 -0
- package/src/i18n/generated/errors.es.gen.ts +69 -0
- package/src/i18n/generated/errors.gen.ts +214 -0
- package/src/i18n/generated/errors.json +423 -0
- package/src/i18n/generated/errors.ru.gen.ts +69 -0
- package/src/i18n/keys.ts +220 -0
- package/src/i18n/ru.ts +126 -0
- package/src/index.ts +154 -0
- package/src/model/context.tsx +28 -0
- package/src/model/queries.ts +79 -0
- package/src/model/queryKeys.ts +48 -0
- package/src/model/runtime.ts +33 -0
- package/src/nav/manifest.ts +53 -0
- package/src/router/index.ts +38 -0
- package/src/state/degradations.ts +75 -0
- package/src/state/facets.ts +219 -0
- package/src/state/urlState.ts +458 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types for the stapel-search HTTP contract — **derived from the
|
|
3
|
+
* generated OpenAPI surface** (frontend-standard §2/§3), never hand-maintained.
|
|
4
|
+
* The single source of truth is `components["schemas"]` from this pair's own
|
|
5
|
+
* package-LOCAL generated schema (`./generated/schema.js`, produced by
|
|
6
|
+
* `pnpm gen:api` from stapel-search's OWN `docs/schema.json`).
|
|
7
|
+
*
|
|
8
|
+
* Where drf-spectacular under-describes the runtime, this file adds a small
|
|
9
|
+
* documented correction and states what the generator lost.
|
|
10
|
+
*/
|
|
11
|
+
import type { components } from "./generated/schema.js";
|
|
12
|
+
/** The generated schema table — the one source of truth for wire shapes. */
|
|
13
|
+
export type Schemas = components["schemas"];
|
|
14
|
+
/** `GET /query` 200 — the whole envelope. */
|
|
15
|
+
export type SearchResponse = Schemas["SearchResponse"];
|
|
16
|
+
/** One result row. `promoted` is present on EVERY item under EVERY sort — a
|
|
17
|
+
* mandatory marking (DSA Art. 26), not an optional field, which is why the
|
|
18
|
+
* card slot's contract carries it and why the default skin renders it. */
|
|
19
|
+
export type SearchItem = Schemas["SearchItem"];
|
|
20
|
+
/** The honesty block beside the counts: `approximate`, `candidates`,
|
|
21
|
+
* `counted`, `skipped`. Rendered, never swallowed (spec §4.2). */
|
|
22
|
+
export type FacetMeta = Schemas["FacetMeta"];
|
|
23
|
+
/** `GET /suggest` 200. */
|
|
24
|
+
export type SuggestResponse = Schemas["SuggestResponse"];
|
|
25
|
+
/** `GET /ranking` 200 — the P2B Art. 5 disclosure. */
|
|
26
|
+
export type RankingResponse = Schemas["RankingResponse"];
|
|
27
|
+
/** One ranking parameter of the disclosure. */
|
|
28
|
+
export type Scorer = Schemas["Scorer"];
|
|
29
|
+
/**
|
|
30
|
+
* The sorts stapel-search ships (`conf.py::DEFAULT_SORTS`).
|
|
31
|
+
*
|
|
32
|
+
* GENERATOR NOTE, and the reason `sort` is typed as a plain `string` below:
|
|
33
|
+
* `docs/schema.json` declares **no `enum`** on any query parameter — the sort
|
|
34
|
+
* vocabulary lives in the backend's `SORTS` setting, which a deployment may
|
|
35
|
+
* extend. So this list is what the shipped default offers a sort control, NOT
|
|
36
|
+
* a claim about what the server will accept. An unknown value is refused by
|
|
37
|
+
* the server with `error.400.search_unknown_sort`, naming the value; a client
|
|
38
|
+
* union that silently dropped it would turn a shareable link into an empty
|
|
39
|
+
* page.
|
|
40
|
+
*/
|
|
41
|
+
export declare const SEARCH_SORTS: readonly string[];
|
|
42
|
+
/** Which facets to count: the category's own plan (`"on"`/omitted), none
|
|
43
|
+
* (`"off"`), or an explicit slug list. */
|
|
44
|
+
export type FacetSelection = "on" | "off" | readonly string[];
|
|
45
|
+
/** One `r.<slug>` range. Either end may be absent — `..500` and `100..` are
|
|
46
|
+
* both legal, and both ends absent means "no range at all". */
|
|
47
|
+
export interface SearchRange {
|
|
48
|
+
readonly from?: string;
|
|
49
|
+
readonly to?: string;
|
|
50
|
+
}
|
|
51
|
+
/** A point plus an optional radius (`lat`/`lon`/`radius_km`). */
|
|
52
|
+
export interface SearchGeoCenter {
|
|
53
|
+
readonly kind: "center";
|
|
54
|
+
readonly lat: number;
|
|
55
|
+
readonly lon: number;
|
|
56
|
+
readonly radiusKm?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A bounding box (`bbox=minLat,minLon,maxLat,maxLon`). `minLon > maxLon` is
|
|
60
|
+
* LEGAL and means the box crosses the antimeridian — the codec must not
|
|
61
|
+
* "fix" it, and the backend explicitly allows it (`query.py`).
|
|
62
|
+
*/
|
|
63
|
+
export interface SearchGeoBox {
|
|
64
|
+
readonly kind: "bbox";
|
|
65
|
+
readonly minLat: number;
|
|
66
|
+
readonly minLon: number;
|
|
67
|
+
readonly maxLat: number;
|
|
68
|
+
readonly maxLon: number;
|
|
69
|
+
}
|
|
70
|
+
/** Either geo form. `bbox` wins server-side when both are sent, so the state
|
|
71
|
+
* model carries one or the other and never both. */
|
|
72
|
+
export type SearchGeo = SearchGeoCenter | SearchGeoBox;
|
|
73
|
+
/**
|
|
74
|
+
* The full query state — everything the URL carries, in one value.
|
|
75
|
+
*
|
|
76
|
+
* `type` is the only required parameter of the endpoint (`error.400
|
|
77
|
+
* .search_unknown_doc_type` names it when absent or unregistered).
|
|
78
|
+
*/
|
|
79
|
+
export interface SearchQueryState {
|
|
80
|
+
/** Registered doc type. One type per query; federated search is not in v1. */
|
|
81
|
+
readonly type: string;
|
|
82
|
+
/** Free text. `""` means "no text", which is a valid search (browse). */
|
|
83
|
+
readonly q: string;
|
|
84
|
+
/** Language of the query: picks the analyzer AND narrows the corpus. */
|
|
85
|
+
readonly lang?: string;
|
|
86
|
+
/** `root/leaf` path. A PREFIX filter — a parent finds its descendants. */
|
|
87
|
+
readonly category?: string;
|
|
88
|
+
/** Opaque owner key — the seller's own listings. */
|
|
89
|
+
readonly owner?: string;
|
|
90
|
+
/** `f.<slug>` → the chosen values. Repeat = OR within a slug; different
|
|
91
|
+
* slugs AND together. An empty array is the same as no entry. */
|
|
92
|
+
readonly filters: Readonly<Record<string, readonly string[]>>;
|
|
93
|
+
/** `r.<slug>` → `from..to`. */
|
|
94
|
+
readonly ranges: Readonly<Record<string, SearchRange>>;
|
|
95
|
+
readonly geo?: SearchGeo;
|
|
96
|
+
/** Omitted lets the server choose (`relevance` with text, else `newest`). */
|
|
97
|
+
readonly sort?: string;
|
|
98
|
+
readonly facets?: FacetSelection;
|
|
99
|
+
/** Opaque keyset cursor from a previous answer. */
|
|
100
|
+
readonly anchor?: string;
|
|
101
|
+
readonly direction?: "next" | "prev";
|
|
102
|
+
readonly limit?: number;
|
|
103
|
+
}
|
|
104
|
+
/** Request shape for `GET /suggest` (typed, but deliberately not hooked —
|
|
105
|
+
* see `searchApi.ts`). */
|
|
106
|
+
export interface SuggestParams {
|
|
107
|
+
readonly type: string;
|
|
108
|
+
readonly q?: string;
|
|
109
|
+
readonly limit?: number;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* One thing the engine could not do for this query, parsed out of the
|
|
113
|
+
* envelope's `degraded[]`.
|
|
114
|
+
*
|
|
115
|
+
* The backend concatenates its own contribution with the backend's and the
|
|
116
|
+
* facet counter's WITHOUT de-duplicating (`services.py`), so the same literal
|
|
117
|
+
* can arrive twice; {@link SearchDegradation} values are de-duplicated by
|
|
118
|
+
* `raw` when parsed.
|
|
119
|
+
*/
|
|
120
|
+
export type SearchDegradationKind = "typo_tolerance" | "phrase_synonyms" | "exact_total" | "exact_facet_counts" | "category_rollup" | "scorer" | "unknown";
|
|
121
|
+
export interface SearchDegradation {
|
|
122
|
+
readonly kind: SearchDegradationKind;
|
|
123
|
+
/** The literal the server sent — shown for an `unknown` kind, because a
|
|
124
|
+
* degradation this build has no sentence for is still a degradation. */
|
|
125
|
+
readonly raw: string;
|
|
126
|
+
/** For `kind: "scorer"`, the scorer slug after the `scorer:` prefix. */
|
|
127
|
+
readonly scorer?: string;
|
|
128
|
+
/** i18n key for the sentence. */
|
|
129
|
+
readonly messageKey: string;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sorts stapel-search ships (`conf.py::DEFAULT_SORTS`).
|
|
3
|
+
*
|
|
4
|
+
* GENERATOR NOTE, and the reason `sort` is typed as a plain `string` below:
|
|
5
|
+
* `docs/schema.json` declares **no `enum`** on any query parameter — the sort
|
|
6
|
+
* vocabulary lives in the backend's `SORTS` setting, which a deployment may
|
|
7
|
+
* extend. So this list is what the shipped default offers a sort control, NOT
|
|
8
|
+
* a claim about what the server will accept. An unknown value is refused by
|
|
9
|
+
* the server with `error.400.search_unknown_sort`, naming the value; a client
|
|
10
|
+
* union that silently dropped it would turn a shareable link into an empty
|
|
11
|
+
* page.
|
|
12
|
+
*/
|
|
13
|
+
export const SEARCH_SORTS = [
|
|
14
|
+
"relevance",
|
|
15
|
+
"newest",
|
|
16
|
+
"price_asc",
|
|
17
|
+
"price_desc",
|
|
18
|
+
"distance",
|
|
19
|
+
];
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AAoCA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAsB;IAC7C,WAAW;IACX,QAAQ;IACR,WAAW;IACX,YAAY;IACZ,UAAU;CACX,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `degraded[]`, on the screen.
|
|
3
|
+
*
|
|
4
|
+
* The whole point of the backend declaring its degradations per query is that
|
|
5
|
+
* a client can tell the person. A banner is not decoration here: "counts are
|
|
6
|
+
* approximate" and "subcategories may be missing" change what the page MEANS,
|
|
7
|
+
* and the spec calls swallowing them the same class of defect as `data ?? []`.
|
|
8
|
+
*
|
|
9
|
+
* An `unknown` degradation still renders — with the raw literal, because a
|
|
10
|
+
* build that predates a new limitation should say "the engine reported
|
|
11
|
+
* something we have no wording for: X", not nothing at all.
|
|
12
|
+
*/
|
|
13
|
+
import type { ReactElement } from "react";
|
|
14
|
+
import type { SearchDegradation } from "../api/types.js";
|
|
15
|
+
export interface DegradationNoticeProps {
|
|
16
|
+
readonly degradations: readonly SearchDegradation[];
|
|
17
|
+
}
|
|
18
|
+
export declare function DegradationNotice(props: DegradationNoticeProps): ReactElement | null;
|
|
19
|
+
//# sourceMappingURL=DegradationNotice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DegradationNotice.d.ts","sourceRoot":"","sources":["../../src/default/DegradationNotice.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACrD;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,sBAAsB,GAC5B,YAAY,GAAG,IAAI,CAyBrB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, Typography } from "antd";
|
|
3
|
+
import { useT } from "@stapel/core";
|
|
4
|
+
import { SEARCH_I18N_KEYS } from "../i18n/keys.js";
|
|
5
|
+
export function DegradationNotice(props) {
|
|
6
|
+
const t = useT();
|
|
7
|
+
if (props.degradations.length === 0)
|
|
8
|
+
return null;
|
|
9
|
+
return (_jsx(Alert, { type: "warning", showIcon: true, "data-testid": "search-degraded", message: t(SEARCH_I18N_KEYS.degradedTitle), description: _jsx("ul", { style: { margin: 0, paddingInlineStart: 20 }, children: props.degradations.map((degradation) => (_jsx("li", { "data-degradation": degradation.raw, children: _jsx(Typography.Text, { type: "secondary", children: t(degradation.messageKey, {
|
|
10
|
+
scorer: degradation.scorer ?? "",
|
|
11
|
+
raw: degradation.raw,
|
|
12
|
+
}) }) }, degradation.raw))) }) }));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=DegradationNotice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DegradationNotice.js","sourceRoot":"","sources":["../../src/default/DegradationNotice.tsx"],"names":[],"mappings":";AAaA,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAMnD,MAAM,UAAU,iBAAiB,CAC/B,KAA6B;IAE7B,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACjD,OAAO,CACL,KAAC,KAAK,IACJ,IAAI,EAAC,SAAS,EACd,QAAQ,uBACI,iBAAiB,EAC7B,OAAO,EAAE,CAAC,CAAC,gBAAgB,CAAC,aAAa,CAAC,EAC1C,WAAW,EACT,aAAI,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,EAAE,EAAE,YAC7C,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CACvC,iCAA4C,WAAW,CAAC,GAAG,YACzD,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,YAC9B,CAAC,CAAC,WAAW,CAAC,UAAU,EAAE;wBACzB,MAAM,EAAE,WAAW,CAAC,MAAM,IAAI,EAAE;wBAChC,GAAG,EAAE,WAAW,CAAC,GAAG;qBACrB,CAAC,GACc,IANX,WAAW,CAAC,GAAG,CAOnB,CACN,CAAC,GACC,GAEP,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CSSProperties, ReactElement, ReactNode } from "react";
|
|
2
|
+
import type { FlowErrorDisplay } from "@stapel/core";
|
|
3
|
+
export declare function ErrorAlert(props: {
|
|
4
|
+
error: FlowErrorDisplay | undefined;
|
|
5
|
+
style?: CSSProperties | undefined;
|
|
6
|
+
testId?: string | undefined;
|
|
7
|
+
action?: ReactNode;
|
|
8
|
+
}): ReactElement | null;
|
|
9
|
+
//# sourceMappingURL=ErrorAlert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorAlert.d.ts","sourceRoot":"","sources":["../../src/default/ErrorAlert.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACpC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,GAAG,YAAY,GAAG,IAAI,CAsBtB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The one error surface this pair's default skin renders, so the split copy
|
|
4
|
+
* core produces reaches a screen the same way everywhere (mirrors the
|
|
5
|
+
* docs/notifications/workspaces etalon).
|
|
6
|
+
*
|
|
7
|
+
* Core's `describeFlowError` splits a failure into the sentence a person
|
|
8
|
+
* reads and the technical detail a support agent quotes (owner report
|
|
9
|
+
* 2026-08-09: the status used to be spliced into the sentence, ending in a
|
|
10
|
+
* bare `" (500)"`). The split only helps if a skin renders both halves at
|
|
11
|
+
* their own weights: `message` at normal weight, `detail` muted and small.
|
|
12
|
+
*
|
|
13
|
+
* `undefined` in — including a `detail` core left `undefined` because there
|
|
14
|
+
* was nothing worth quoting — renders nothing rather than an empty line.
|
|
15
|
+
*/
|
|
16
|
+
import { Alert, Typography } from "antd";
|
|
17
|
+
export function ErrorAlert(props) {
|
|
18
|
+
const { error } = props;
|
|
19
|
+
if (!error)
|
|
20
|
+
return null;
|
|
21
|
+
return (_jsx(Alert, { type: "error", showIcon: true, ...(props.style ? { style: props.style } : {}), ...(props.testId ? { "data-testid": props.testId } : {}), ...(props.action ? { action: props.action } : {}), message: error.message, ...(error.detail
|
|
22
|
+
? {
|
|
23
|
+
description: (_jsx(Typography.Text, { type: "secondary", style: { fontSize: 12 }, children: error.detail })),
|
|
24
|
+
}
|
|
25
|
+
: {}) }));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=ErrorAlert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorAlert.js","sourceRoot":"","sources":["../../src/default/ErrorAlert.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIzC,MAAM,UAAU,UAAU,CAAC,KAK1B;IACC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACxB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,CACL,KAAC,KAAK,IACJ,IAAI,EAAC,OAAO,EACZ,QAAQ,WACJ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAC3C,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KACrD,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAClD,OAAO,EAAE,KAAK,CAAC,OAAO,KAClB,CAAC,KAAK,CAAC,MAAM;YACf,CAAC,CAAC;gBACE,WAAW,EAAE,CACX,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,EAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,YACtD,KAAK,CAAC,MAAM,GACG,CACnB;aACF;YACH,CAAC,CAAC,EAAE,CAAC,GACP,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<FacetPanelPane>` — the antd facet panel.
|
|
3
|
+
*
|
|
4
|
+
* Three things it is obliged to render, all of which a naive panel drops:
|
|
5
|
+
*
|
|
6
|
+
* - the count NEXT TO EVERY OPTION, including the ones you have not chosen.
|
|
7
|
+
* Facets are counted with their own filter removed, so those numbers are
|
|
8
|
+
* "what you would get by switching to this instead" — a sibling that shows
|
|
9
|
+
* a stale or zeroed count has converted a drill-down facet into a naive one.
|
|
10
|
+
* - `approximate` — said in words, from the first day, because the counts
|
|
11
|
+
* genuinely are a sample above the backend's candidate cap.
|
|
12
|
+
* - `skipped` — the slugs the server did not count at all. Their options show
|
|
13
|
+
* "not counted", never `0`. A silent zero there is the same defect class as
|
|
14
|
+
* `data ?? []`: a number that looks like an answer and is not one.
|
|
15
|
+
*/
|
|
16
|
+
import type { ReactElement } from "react";
|
|
17
|
+
import type { FeatureDef } from "@stapel/attributes-react";
|
|
18
|
+
import type { ThemeModeProp } from "./types.js";
|
|
19
|
+
export interface FacetPanelPaneProps extends ThemeModeProp {
|
|
20
|
+
/** The category's feature schema — the source of option LABELS. */
|
|
21
|
+
readonly categoryFeatures?: readonly FeatureDef[];
|
|
22
|
+
readonly locale?: string;
|
|
23
|
+
readonly enabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function FacetPanelPane(props: FacetPanelPaneProps): ReactElement;
|
|
26
|
+
//# sourceMappingURL=FacetPanelPane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FacetPanelPane.d.ts","sourceRoot":"","sources":["../../src/default/FacetPanelPane.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAM3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAsCD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CAwGvE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, Button, Checkbox, Empty, Flex, Spin, Tag, Typography } from "antd";
|
|
3
|
+
import { matchList, toFlowError, useDescribeFlowError, useT } from "@stapel/core";
|
|
4
|
+
import { FacetPanel } from "../headless/FacetPanel.js";
|
|
5
|
+
import { SEARCH_I18N_KEYS } from "../i18n/keys.js";
|
|
6
|
+
import { ErrorAlert } from "./ErrorAlert.js";
|
|
7
|
+
import { SearchSkinTheme } from "./theme.js";
|
|
8
|
+
function OptionRow(props) {
|
|
9
|
+
const t = useT();
|
|
10
|
+
const { option, group } = props;
|
|
11
|
+
return (_jsxs(Flex, { justify: "space-between", align: "center", gap: 8, children: [_jsx(Checkbox, { checked: option.selected, "data-testid": `facet-option-${group.slug}-${option.value}`, "data-analytics": "none", "data-analytics-reason": "a filter is a read, not a flow step", onChange: () => {
|
|
12
|
+
props.onToggle(group.slug, option.value);
|
|
13
|
+
}, children: option.label }), option.count === null ? (_jsx(Tag, { "data-testid": `facet-count-${group.slug}-${option.value}`, children: t(SEARCH_I18N_KEYS.facetsNotCounted) })) : (_jsx(Typography.Text, { type: "secondary", "data-testid": `facet-count-${group.slug}-${option.value}`, children: option.count }))] }));
|
|
14
|
+
}
|
|
15
|
+
export function FacetPanelPane(props) {
|
|
16
|
+
const t = useT();
|
|
17
|
+
const describe = useDescribeFlowError();
|
|
18
|
+
return (_jsx(SearchSkinTheme, { ...(props.mode !== undefined ? { mode: props.mode } : {}), children: _jsx(FacetPanel, { ...(props.categoryFeatures !== undefined
|
|
19
|
+
? { categoryFeatures: props.categoryFeatures }
|
|
20
|
+
: {}), ...(props.locale !== undefined ? { locale: props.locale } : {}), ...(props.enabled !== undefined ? { enabled: props.enabled } : {}), children: (bag) => (_jsxs(Flex, { vertical: true, gap: 12, "data-testid": "search-facets", children: [_jsxs(Flex, { justify: "space-between", align: "center", gap: 8, children: [_jsx(Typography.Title, { level: 5, style: { margin: 0 }, children: t(SEARCH_I18N_KEYS.facetsTitle) }), bag.activeFilters > 0 && (_jsx(Button, { size: "small", onClick: bag.clearAll, "data-analytics": "none", "data-analytics-reason": "a filter is a read, not a flow step", "data-testid": "facets-clear-all", children: t(SEARCH_I18N_KEYS.facetsClearAll, { count: bag.activeFilters }) }))] }), bag.approximate && (_jsx(Alert, { type: "info", showIcon: true, "data-testid": "facets-approximate", message: t(SEARCH_I18N_KEYS.facetsApproximate) })), bag.skipped.length > 0 && (_jsx(Alert, { type: "warning", showIcon: true, "data-testid": "facets-skipped", message: t(SEARCH_I18N_KEYS.facetsSkipped, {
|
|
21
|
+
slugs: bag.skipped.join(", "),
|
|
22
|
+
}) })), matchList(bag.state, {
|
|
23
|
+
loading: () => (_jsx(Flex, { justify: "center", style: { padding: 16 }, children: _jsx(Spin, { "data-testid": "facets-loading" }) })),
|
|
24
|
+
failed: (error) => (_jsx(ErrorAlert, { testId: "facets-failed", error: {
|
|
25
|
+
...describe(toFlowError(error)),
|
|
26
|
+
message: t(SEARCH_I18N_KEYS.facetsLoadFailed),
|
|
27
|
+
} })),
|
|
28
|
+
empty: () => (_jsx(Empty, { "data-testid": "facets-empty", description: t(SEARCH_I18N_KEYS.facetsEmpty) })),
|
|
29
|
+
ready: (groups) => (_jsxs(Flex, { vertical: true, gap: 16, children: [groups.map((group) => (_jsxs(Flex, { vertical: true, gap: 4, "data-testid": `facet-group-${group.slug}`, "data-counted": group.counted ? "true" : "false", children: [_jsx(Typography.Text, { strong: true, children: group.label }), group.options.map((option) => (_jsx(OptionRow, { group: group, option: option, onToggle: bag.toggle }, option.value)))] }, group.slug))), _jsx(Typography.Text, { type: "secondary", style: { fontSize: 12 }, children: t(SEARCH_I18N_KEYS.facetsDrillDownHint) })] })),
|
|
30
|
+
})] })) }) }));
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=FacetPanelPane.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FacetPanelPane.js","sourceRoot":"","sources":["../../src/default/FacetPanelPane.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<RankingDisclosurePane>` — the P2B Art. 5 page: which parameters decide
|
|
3
|
+
* the order of results, their relative weight, and which of them the
|
|
4
|
+
* configured engine cannot actually evaluate.
|
|
5
|
+
*
|
|
6
|
+
* Generated from the backend's scorer registry, so it cannot drift from the
|
|
7
|
+
* ranking it describes — which is the only version of this page worth
|
|
8
|
+
* shipping.
|
|
9
|
+
*/
|
|
10
|
+
import type { ReactElement } from "react";
|
|
11
|
+
import type { ThemeModeProp } from "./types.js";
|
|
12
|
+
export interface RankingDisclosurePaneProps extends ThemeModeProp {
|
|
13
|
+
/** Doc type to disclose; omitted asks for the deployment's default. */
|
|
14
|
+
readonly type?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function RankingDisclosurePane(props: RankingDisclosurePaneProps): ReactElement;
|
|
17
|
+
//# sourceMappingURL=RankingDisclosurePane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RankingDisclosurePane.d.ts","sourceRoot":"","sources":["../../src/default/RankingDisclosurePane.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAQ1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,0BAA2B,SAAQ,aAAa;IAC/D,uEAAuE;IACvE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,0BAA0B,GAChC,YAAY,CAyFd"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Empty, Flex, List, Spin, Tag, Typography } from "antd";
|
|
3
|
+
import { matchList, toFlowError, useDescribeFlowError, useT } from "@stapel/core";
|
|
4
|
+
import { RankingDisclosure } from "../headless/RankingDisclosure.js";
|
|
5
|
+
import { SEARCH_I18N_KEYS } from "../i18n/keys.js";
|
|
6
|
+
import { ErrorAlert } from "./ErrorAlert.js";
|
|
7
|
+
import { SearchSkinTheme } from "./theme.js";
|
|
8
|
+
export function RankingDisclosurePane(props) {
|
|
9
|
+
const t = useT();
|
|
10
|
+
const describe = useDescribeFlowError();
|
|
11
|
+
return (_jsx(SearchSkinTheme, { ...(props.mode !== undefined ? { mode: props.mode } : {}), children: _jsx(RankingDisclosure, { ...(props.type !== undefined ? { type: props.type } : {}), children: (bag) => (_jsxs(Flex, { vertical: true, gap: 12, "data-testid": "search-ranking", children: [_jsx(Typography.Title, { level: 4, style: { margin: 0 }, children: t(SEARCH_I18N_KEYS.rankingTitle) }), _jsx(Typography.Paragraph, { type: "secondary", style: { margin: 0 }, children: t(SEARCH_I18N_KEYS.rankingIntro) }), matchList(bag.state, {
|
|
12
|
+
loading: () => (_jsx(Flex, { justify: "center", style: { padding: 16 }, children: _jsx(Spin, { "data-testid": "ranking-loading" }) })),
|
|
13
|
+
failed: (error) => (_jsx(ErrorAlert, { testId: "ranking-failed", error: {
|
|
14
|
+
...describe(toFlowError(error)),
|
|
15
|
+
message: t(SEARCH_I18N_KEYS.rankingLoadFailed),
|
|
16
|
+
} })),
|
|
17
|
+
empty: () => (_jsx(Empty, { "data-testid": "ranking-empty", description: t(SEARCH_I18N_KEYS.rankingEmpty) })),
|
|
18
|
+
ready: (scorers) => (_jsx(List, { "data-testid": "ranking-list", dataSource: [...scorers], renderItem: (scorer) => (_jsx(List.Item, { "data-scorer": scorer.slug, children: _jsxs(Flex, { vertical: true, gap: 2, style: { width: "100%" }, children: [_jsxs(Flex, { justify: "space-between", align: "center", gap: 8, children: [_jsx(Typography.Text, { strong: true, children: t(scorer.description_key) }), _jsx(Tag, { children: scorer.weight })] }), _jsx(Typography.Text, { type: "secondary", children: scorer.description }), _jsxs(Typography.Text, { type: "secondary", style: { fontSize: 12 }, children: [t(SEARCH_I18N_KEYS.rankingAppliesTo), ": ", scorer.applies_to_sorts.join(", ")] }), !scorer.active && (_jsx(Typography.Text, { type: "warning", "data-testid": "ranking-inactive", children: t(SEARCH_I18N_KEYS.rankingInactive, {
|
|
19
|
+
reason: scorer.inactive_reason,
|
|
20
|
+
}) }))] }) }, scorer.slug)) })),
|
|
21
|
+
}), bag.notes.length > 0 && (_jsxs(Flex, { vertical: true, gap: 2, children: [_jsx(Typography.Text, { strong: true, children: t(SEARCH_I18N_KEYS.rankingNotes) }), bag.notes.map((note) => (_jsx(Typography.Text, { type: "secondary", children: note }, note)))] }))] })) }) }));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=RankingDisclosurePane.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RankingDisclosurePane.js","sourceRoot":"","sources":["../../src/default/RankingDisclosurePane.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<SearchPage>` — the composed screen the nav manifest points at: filters on
|
|
3
|
+
* the left, sort and results on the right, the ranking link in the footer.
|
|
4
|
+
*
|
|
5
|
+
* It composes the two panes and owns NO state of its own. The URL is the
|
|
6
|
+
* state, and it arrives through the `adapter` prop — which is where a host
|
|
7
|
+
* hands in `useRouterSearchParams()` from `@stapel/search-react/router`, or
|
|
8
|
+
* its own binding. The page does not reach for a router, so the same
|
|
9
|
+
* component renders under react-router, under a Next.js app router, and in a
|
|
10
|
+
* test with a plain `URLSearchParams`.
|
|
11
|
+
*
|
|
12
|
+
* The three seams a storefront fills (spec §6.2 items 1–3): `renderCard`
|
|
13
|
+
* (a `<ListingCard>`), `categoryFeatures` (from categories-react, for facet
|
|
14
|
+
* labels), and `footer` (the container's own chrome). None of them is
|
|
15
|
+
* optional behaviour in disguise — every one has a working default.
|
|
16
|
+
*/
|
|
17
|
+
import type { ReactElement, ReactNode } from "react";
|
|
18
|
+
import type { FeatureDef } from "@stapel/attributes-react";
|
|
19
|
+
import type { SearchParamsAdapter } from "../headless/SearchStateProvider.js";
|
|
20
|
+
import type { ParseSearchStateOptions } from "../state/urlState.js";
|
|
21
|
+
import type { SearchCardRenderer } from "./SearchResultCard.js";
|
|
22
|
+
import type { ThemeModeProp } from "./types.js";
|
|
23
|
+
export interface SearchPageProps extends ThemeModeProp, ParseSearchStateOptions {
|
|
24
|
+
/** The URL binding. `useRouterSearchParams()` from `./router` is the
|
|
25
|
+
* react-router one. */
|
|
26
|
+
readonly adapter: SearchParamsAdapter;
|
|
27
|
+
readonly renderCard?: SearchCardRenderer;
|
|
28
|
+
readonly categoryFeatures?: readonly FeatureDef[];
|
|
29
|
+
readonly locale?: string;
|
|
30
|
+
/** Container chrome under the results — e.g. the ranking-disclosure link. */
|
|
31
|
+
readonly footer?: ReactNode;
|
|
32
|
+
}
|
|
33
|
+
export declare function SearchPage(props: SearchPageProps): ReactElement;
|
|
34
|
+
//# sourceMappingURL=SearchPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchPage.d.ts","sourceRoot":"","sources":["../../src/default/SearchPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAKpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,eAAgB,SAAQ,aAAa,EAAE,uBAAuB;IAC7E;2BACuB;IACvB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IACzC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,CA2C/D"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Col, Flex, Row, Typography } from "antd";
|
|
3
|
+
import { useT } from "@stapel/core";
|
|
4
|
+
import { SearchStateProvider } from "../headless/SearchStateProvider.js";
|
|
5
|
+
import { SEARCH_I18N_KEYS } from "../i18n/keys.js";
|
|
6
|
+
import { FacetPanelPane } from "./FacetPanelPane.js";
|
|
7
|
+
import { SearchResultsPane } from "./SearchResultsPane.js";
|
|
8
|
+
import { SortSelect } from "./SortSelect.js";
|
|
9
|
+
import { SearchSkinTheme } from "./theme.js";
|
|
10
|
+
import { UrlIssueNotice } from "./UrlIssueNotice.js";
|
|
11
|
+
export function SearchPage(props) {
|
|
12
|
+
const t = useT();
|
|
13
|
+
const { adapter, renderCard, categoryFeatures, locale, footer, mode, ...parseOptions } = props;
|
|
14
|
+
return (_jsx(SearchSkinTheme, { ...(mode !== undefined ? { mode } : {}), children: _jsx(SearchStateProvider, { adapter: adapter, ...parseOptions, children: _jsxs(Flex, { vertical: true, gap: 16, "data-testid": "search-page", children: [_jsx(UrlIssueNotice, {}), _jsxs(Row, { gutter: [16, 16], children: [_jsx(Col, { xs: 24, md: 7, children: _jsx(FacetPanelPane, { ...(categoryFeatures !== undefined ? { categoryFeatures } : {}), ...(locale !== undefined ? { locale } : {}) }) }), _jsx(Col, { xs: 24, md: 17, children: _jsxs(Flex, { vertical: true, gap: 12, children: [_jsxs(Flex, { justify: "space-between", align: "center", wrap: true, gap: 8, children: [_jsx(Typography.Text, { type: "secondary", children: t(SEARCH_I18N_KEYS.resultsTitle) }), _jsx(SortSelect, {})] }), _jsx(SearchResultsPane, { ...(renderCard !== undefined ? { renderCard } : {}), ...(footer !== undefined ? { footer } : {}) })] }) })] })] }) }) }));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=SearchPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchPage.js","sourceRoot":"","sources":["../../src/default/SearchPage.tsx"],"names":[],"mappings":";AAiBA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAGzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAcrD,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,EACJ,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,MAAM,EACN,IAAI,EACJ,GAAG,YAAY,EAChB,GAAG,KAAK,CAAC;IAEV,OAAO,CACL,KAAC,eAAe,OAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YACvD,KAAC,mBAAmB,IAAC,OAAO,EAAE,OAAO,KAAM,YAAY,YACrD,MAAC,IAAI,IAAC,QAAQ,QAAC,GAAG,EAAE,EAAE,iBAAc,aAAa,aAC/C,KAAC,cAAc,KAAG,EAClB,MAAC,GAAG,IAAC,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,aACnB,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,YAChB,KAAC,cAAc,OACT,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAC5D,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAC5C,GACE,EACN,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,YACjB,MAAC,IAAI,IAAC,QAAQ,QAAC,GAAG,EAAE,EAAE,aACpB,MAAC,IAAI,IAAC,OAAO,EAAC,eAAe,EAAC,KAAK,EAAC,QAAQ,EAAC,IAAI,QAAC,GAAG,EAAE,CAAC,aACtD,KAAC,UAAU,CAAC,IAAI,IAAC,IAAI,EAAC,WAAW,YAC9B,CAAC,CAAC,gBAAgB,CAAC,YAAY,CAAC,GACjB,EAClB,KAAC,UAAU,KAAG,IACT,EACP,KAAC,iBAAiB,OACZ,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAChD,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAC5C,IACG,GACH,IACF,IACD,GACa,GACN,CACnB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GENERIC card — the fallback behind `renderCard`, and the default the
|
|
3
|
+
* spec (§3.7) says the pair must ship.
|
|
4
|
+
*
|
|
5
|
+
* `SearchItem.card` is a free-form object: "stored row fields, so a result
|
|
6
|
+
* page costs one query". Which fields it holds is the DOC TYPE's business,
|
|
7
|
+
* not search's, so this card reads a small, documented set of conventional
|
|
8
|
+
* names and shows what it finds. A storefront replaces the whole thing by
|
|
9
|
+
* passing `renderCard` and rendering `<ListingCard>` from
|
|
10
|
+
* `@stapel/listings-react/default` — that is the slot seam, and the reason
|
|
11
|
+
* these two L2 pairs never import each other.
|
|
12
|
+
*
|
|
13
|
+
* The ONE thing a replacement card may not drop is the `promoted` marking:
|
|
14
|
+
* DSA Art. 26 makes it mandatory, the serializer puts it on every item under
|
|
15
|
+
* every sort, and a storefront where some lists show it and some do not is
|
|
16
|
+
* legally worse than one that never showed it. `renderCard` receives the whole
|
|
17
|
+
* item so the marking is always reachable, and this default renders it.
|
|
18
|
+
*/
|
|
19
|
+
import type { ReactElement, ReactNode } from "react";
|
|
20
|
+
import type { SearchItem } from "../api/types.js";
|
|
21
|
+
/** What the card slot is handed. */
|
|
22
|
+
export interface SearchCardProps {
|
|
23
|
+
readonly item: SearchItem;
|
|
24
|
+
}
|
|
25
|
+
/** A card renderer a host supplies for `renderCard`. */
|
|
26
|
+
export type SearchCardRenderer = (item: SearchItem) => ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* The conventional field names this default reads off `card`. Documented
|
|
29
|
+
* rather than guessed at each site, and deliberately short: anything richer
|
|
30
|
+
* belongs in the owning pair's own card.
|
|
31
|
+
*/
|
|
32
|
+
export declare const GENERIC_CARD_FIELDS: readonly string[];
|
|
33
|
+
export declare function SearchResultCard(props: SearchCardProps): ReactElement;
|
|
34
|
+
//# sourceMappingURL=SearchResultCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchResultCard.d.ts","sourceRoot":"","sources":["../../src/default/SearchResultCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAED,wDAAwD;AACxD,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,SAAS,CAAC;AAQjE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAMhD,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,CA4CrE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Card, Flex, Tag, Tooltip, Typography } from "antd";
|
|
3
|
+
import { useT } from "@stapel/core";
|
|
4
|
+
import { SEARCH_I18N_KEYS } from "../i18n/keys.js";
|
|
5
|
+
function text(value) {
|
|
6
|
+
if (typeof value === "string" && value.length > 0)
|
|
7
|
+
return value;
|
|
8
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
9
|
+
return String(value);
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The conventional field names this default reads off `card`. Documented
|
|
14
|
+
* rather than guessed at each site, and deliberately short: anything richer
|
|
15
|
+
* belongs in the owning pair's own card.
|
|
16
|
+
*/
|
|
17
|
+
export const GENERIC_CARD_FIELDS = [
|
|
18
|
+
"title",
|
|
19
|
+
"price",
|
|
20
|
+
"currency",
|
|
21
|
+
"location",
|
|
22
|
+
"image_url",
|
|
23
|
+
];
|
|
24
|
+
export function SearchResultCard(props) {
|
|
25
|
+
const t = useT();
|
|
26
|
+
const card = props.item.card;
|
|
27
|
+
const title = text(card["title"]) ?? t(SEARCH_I18N_KEYS.resultsUntitled);
|
|
28
|
+
const price = text(card["price"]);
|
|
29
|
+
const currency = text(card["currency"]);
|
|
30
|
+
const location = text(card["location"]);
|
|
31
|
+
const distance = props.item.distance_km !== null
|
|
32
|
+
? t(SEARCH_I18N_KEYS.resultsDistanceKm, {
|
|
33
|
+
km: props.item.distance_km.toFixed(1),
|
|
34
|
+
})
|
|
35
|
+
: undefined;
|
|
36
|
+
return (_jsx(Card, { size: "small", "data-testid": "search-result-card", "data-promoted": props.item.promoted ? "true" : "false", children: _jsxs(Flex, { vertical: true, gap: 4, children: [_jsxs(Flex, { justify: "space-between", align: "start", gap: 8, children: [_jsx(Typography.Text, { strong: true, children: title }), props.item.promoted && (_jsx(Tooltip, { title: t(SEARCH_I18N_KEYS.resultsPromotedHint), children: _jsx(Tag, { color: "gold", "data-testid": "search-result-promoted", children: t(SEARCH_I18N_KEYS.resultsPromoted) }) }))] }), price !== undefined && (_jsx(Typography.Text, { children: currency !== undefined ? `${price} ${currency}` : price })), (location !== undefined || distance !== undefined) && (_jsx(Typography.Text, { type: "secondary", children: [location, distance].filter((v) => v !== undefined).join(" · ") }))] }) }));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=SearchResultCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchResultCard.js","sourceRoot":"","sources":["../../src/default/SearchResultCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<SearchResultsPane>` — the antd result page: the count, the degradation
|
|
3
|
+
* banner, the cards, and the keyset controls.
|
|
4
|
+
*
|
|
5
|
+
* Rendered through core's `matchList`, whose FOUR required arms are the point.
|
|
6
|
+
* "Nothing matches this search" is reachable only from a search that actually
|
|
7
|
+
* ran; a 5xx renders "we could not run this search" plus a retry. The
|
|
8
|
+
* substitution of one for the other is what cost the 2026-08-09 incident, and
|
|
9
|
+
* the spec's §7.4 negative leg exercises this exact pane against a forced 5xx.
|
|
10
|
+
*
|
|
11
|
+
* The window refusal gets its own sentence. `error.400.search_window_exceeded`
|
|
12
|
+
* is "narrow the search", not "there is nothing here" — it arrives as a 400
|
|
13
|
+
* with an empty body, which is precisely how it would otherwise render as an
|
|
14
|
+
* empty page.
|
|
15
|
+
*/
|
|
16
|
+
import type { ReactElement, ReactNode } from "react";
|
|
17
|
+
import type { SearchCardRenderer } from "./SearchResultCard.js";
|
|
18
|
+
import type { ThemeModeProp } from "./types.js";
|
|
19
|
+
export interface SearchResultsPaneProps extends ThemeModeProp {
|
|
20
|
+
/**
|
|
21
|
+
* The card slot (spec §6.2 item 1). A storefront passes
|
|
22
|
+
* `(item) => <ListingCard …/>`; omitted, the generic card renders.
|
|
23
|
+
* Either way the `promoted` marking is the renderer's obligation and the
|
|
24
|
+
* default honours it.
|
|
25
|
+
*/
|
|
26
|
+
readonly renderCard?: SearchCardRenderer;
|
|
27
|
+
/** Rendered under the pager — where the container puts the ranking link. */
|
|
28
|
+
readonly footer?: ReactNode;
|
|
29
|
+
readonly enabled?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function SearchResultsPane(props: SearchResultsPaneProps): ReactElement;
|
|
32
|
+
//# sourceMappingURL=SearchResultsPane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchResultsPane.d.ts","sourceRoot":"","sources":["../../src/default/SearchResultsPane.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAiBrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IACzC,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AA4CD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,YAAY,CAiF7E"}
|