@zephytiju/prism-search-results 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/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # PrismSearchResultsMicroUI
2
+
3
+ Platform Prism search-results micro-UI. Component id: `search-results`.
4
+ Published to npm as [`@zephytiju/prism-search-results`](https://www.npmjs.com/package/@zephytiju/prism-search-results).
5
+
6
+ Read-only rendering surface for the entity-search contract family: it consumes the channels the
7
+ query-box publishes and never talks to Lattice itself. Render order is fixed — error alert with a
8
+ Retry button, busy-without-results skeletons, guided empty state, then the `{N} MATCHES` header
9
+ and entity cards (dimmed via opacity while a follow-up search is in flight). Composed
10
+ applications (for example Guanlan) consume it as-is; the component is platform-owned.
11
+
12
+ ## Channel contract
13
+
14
+ | Direction | Kind | Id | Payload |
15
+ | --- | --- | --- | --- |
16
+ | consumes | state | `query-box.entity-results` | `EntityResultsProjection \| null` |
17
+ | consumes | state | `query-box.search-status` | `SearchStatus` (imported from `@zephytiju/prism-query-box`) |
18
+ | publishes | state | `search-results.selected-entity` | `{ id, ontologyInterface: "IEntitySummary", label }` on card click |
19
+ | emits | event | `query-box.search-retry` | `null` — the error Retry button asks the query-box to re-run its last request |
20
+
21
+ Channel ids are string literals at every call-site so the build-time channel-graph scanner can
22
+ derive the graph. Channel reads use `usePrismStateValue`; the single publication is setter-only.
23
+
24
+ ## Audit rule
25
+
26
+ Entity search is NOT audit-worthy. This component emits NO audit event. The only event it ever
27
+ emits is the internal `query-box.search-retry` (an error-retry handshake inside the search
28
+ contract family, not an observable domain action).
29
+
30
+ ## Theme
31
+
32
+ No colors are hardcoded. The component uses Mantine components without color props; the color
33
+ theme comes from the host runtime, which wraps the component in its `MantineProvider`. The
34
+ in-flight dimming uses opacity, not a color.
35
+
36
+ ## Local development
37
+
38
+ ```sh
39
+ npm install
40
+ npm run typecheck
41
+ npm test
42
+ npm run dev
43
+ ```
44
+
45
+ `npm install` pulls the platform peers (`@zephytiju/prism-react`,
46
+ `@zephytiju/prism-query-box`, `@zephytiju/lattice-common-interfaces`) from the npm
47
+ registry, along with the host-side peer dependencies (`react`, `react-dom`,
48
+ `@mantine/core`). When consuming the published package, install it directly
49
+ (`npm install @zephytiju/prism-search-results`) and provide those peer dependencies in
50
+ the host application.
51
+
52
+ The demo (`npm run dev`) plays the host: buttons publish sample results, busy, idle, and error
53
+ states onto the query-box channels so every render branch can be inspected in the browser.
54
+
55
+ ## Design record
56
+
57
+ https://qcnwge0wy4s0.feishu.cn/wiki/K40nwA5TZiUE7Nk5hK6cE0W8nTe — §7 (channel contracts) and
58
+ §8 (Lattice transport and interface clients).
@@ -0,0 +1,28 @@
1
+ import type { EntityReference } from "@zephytiju/lattice-common-interfaces";
2
+ /**
3
+ * Payload published on "search-results.selected-entity" when an entity card is
4
+ * clicked — an EntityReference pinned to the IEntitySummary interface.
5
+ */
6
+ export type SelectedEntity = EntityReference & {
7
+ readonly ontologyInterface: "IEntitySummary";
8
+ readonly label: string;
9
+ };
10
+ export interface SearchResultsProps {
11
+ /** Guidance copy for the pre-search empty state. */
12
+ readonly emptyStateHint?: string;
13
+ /** Copy for the error alert title. */
14
+ readonly errorTitle?: string;
15
+ }
16
+ /**
17
+ * Platform Prism search-results micro-UI (component id "search-results").
18
+ *
19
+ * Read-only consumer of the query-box channels; makes NO Lattice calls. Render
20
+ * order: error alert (with retry), busy-without-results skeletons, guided empty
21
+ * state, then the "{N} MATCHES" header and entity cards — dimmed via opacity
22
+ * while a follow-up search is in flight. Selecting a card publishes
23
+ * "search-results.selected-entity"; the error Retry button emits the internal
24
+ * "query-box.search-retry" event so the query-box re-runs its last request.
25
+ * Entity search is NOT audit-worthy: no audit event is ever emitted.
26
+ */
27
+ export declare function SearchResults({ emptyStateHint, errorTitle, }: SearchResultsProps): import("react").JSX.Element;
28
+ //# sourceMappingURL=SearchResults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchResults.d.ts","sourceRoot":"","sources":["../src/SearchResults.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAA2B,MAAM,sCAAsC,CAAC;AAIrG;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG;IAC7C,QAAQ,CAAC,iBAAiB,EAAE,gBAAgB,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAC5B,cAAyC,EACzC,UAAgC,GACjC,EAAE,kBAAkB,+BA6FpB"}
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Alert, Badge, Button, Card, Group, Skeleton, Stack, Text } from "@mantine/core";
3
+ import { emitPrismEvent, usePrismStateSetter, usePrismStateValue } from "@zephytiju/prism-react";
4
+ const DEFAULT_EMPTY_STATE_HINT = "Enter a query in the search box above to find entities. Apply filters to narrow the result set.";
5
+ const DEFAULT_ERROR_TITLE = "Search failed";
6
+ /**
7
+ * Platform Prism search-results micro-UI (component id "search-results").
8
+ *
9
+ * Read-only consumer of the query-box channels; makes NO Lattice calls. Render
10
+ * order: error alert (with retry), busy-without-results skeletons, guided empty
11
+ * state, then the "{N} MATCHES" header and entity cards — dimmed via opacity
12
+ * while a follow-up search is in flight. Selecting a card publishes
13
+ * "search-results.selected-entity"; the error Retry button emits the internal
14
+ * "query-box.search-retry" event so the query-box re-runs its last request.
15
+ * Entity search is NOT audit-worthy: no audit event is ever emitted.
16
+ */
17
+ export function SearchResults({ emptyStateHint = DEFAULT_EMPTY_STATE_HINT, errorTitle = DEFAULT_ERROR_TITLE, }) {
18
+ const results = usePrismStateValue("query-box.entity-results");
19
+ const status = usePrismStateValue("query-box.search-status");
20
+ const publishSelectedEntity = usePrismStateSetter("search-results.selected-entity");
21
+ if (status?.phase === "error") {
22
+ return (_jsx(Alert, { variant: "light", title: errorTitle, "data-testid": "search-results-error", children: _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", "data-testid": "search-results-error-message", children: status.message }), _jsx(Group, { children: _jsx(Button, { variant: "default", "data-testid": "search-results-retry", onClick: () => emitPrismEvent("query-box.search-retry", null), children: "Retry" }) })] }) }));
23
+ }
24
+ if (status?.phase === "busy" && results === null) {
25
+ return (_jsx(Stack, { gap: "sm", "data-testid": "search-results-loading", "aria-busy": "true", children: [0, 1, 2].map((row) => (_jsx(Skeleton, { height: 72, radius: "md" }, row))) }));
26
+ }
27
+ if (results === null) {
28
+ return (_jsx(Card, { withBorder: true, "data-testid": "search-results-empty", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { fw: 500, children: "No results yet" }), _jsx(Text, { size: "sm", children: emptyStateHint })] }) }));
29
+ }
30
+ const inFlight = status?.phase === "busy";
31
+ return (_jsxs(Stack, { gap: "sm", "data-testid": "search-results-list", style: { opacity: inFlight ? 0.5 : 1 }, children: [_jsxs(Group, { justify: "space-between", children: [_jsxs(Text, { fw: 600, "data-testid": "search-results-count", children: [results.resultIds.length, " MATCHES"] }), results.nextCursor !== undefined ? (_jsx(Badge, { variant: "light", size: "sm", children: "more available" })) : null] }), results.items.map((item) => (_jsx(Card, { withBorder: true, "data-testid": `search-result-card-${item.id}`, style: { cursor: "pointer" }, onClick: () => publishSelectedEntity({
32
+ id: item.id,
33
+ ontologyInterface: "IEntitySummary",
34
+ label: item.label,
35
+ }), children: _jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [_jsxs("div", { children: [_jsx(Text, { fw: 500, children: item.label }), _jsx(Text, { size: "sm", children: item.id })] }), _jsx(Badge, { variant: "light", children: item.type })] }) }, item.id)))] }));
36
+ }
@@ -0,0 +1,3 @@
1
+ export { SearchResults } from "./SearchResults.js";
2
+ export type { SearchResultsProps, SelectedEntity } from "./SearchResults.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { SearchResults } from "./SearchResults.js";
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@zephytiju/prism-search-results",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Platform Prism search-results micro-UI (component id \"search-results\"): renders the entity-results and search-status channels published by the query-box — empty, busy, error-with-retry, and match-list states — and publishes the selected entity. No Lattice calls.",
6
+ "license": "UNLICENSED",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "publishConfig": { "access": "public" },
16
+ "files": ["dist"],
17
+ "scripts": {
18
+ "dev": "vite --host 127.0.0.1",
19
+ "build": "tsc -p tsconfig.build.json",
20
+ "typecheck": "tsc --noEmit",
21
+ "test": "vitest run"
22
+ },
23
+ "peerDependencies": {
24
+ "@mantine/core": "^8.1.1",
25
+ "@zephytiju/lattice-common-interfaces": ">=0.1.0",
26
+ "@zephytiju/prism-query-box": ">=0.1.0",
27
+ "@zephytiju/prism-react": ">=0.1.0",
28
+ "react": "^19.1.0",
29
+ "react-dom": "^19.1.0"
30
+ },
31
+ "devDependencies": {
32
+ "@mantine/core": "8.1.1",
33
+ "@testing-library/dom": "^10.4.0",
34
+ "@testing-library/react": "^16.1.0",
35
+ "@types/node": "^22.10.2",
36
+ "@types/react": "^19.1.0",
37
+ "@types/react-dom": "^19.1.0",
38
+ "@zephytiju/lattice-common-interfaces": "0.1.0",
39
+ "@zephytiju/prism-query-box": "0.1.0",
40
+ "@zephytiju/prism-react": "0.1.0",
41
+ "jsdom": "^25.0.1",
42
+ "react": "19.1.0",
43
+ "react-dom": "19.1.0",
44
+ "typescript": "^5.7.2",
45
+ "vite": "6.0.5",
46
+ "vitest": "^2.1.8"
47
+ },
48
+ "engines": {
49
+ "node": ">=20"
50
+ }
51
+ }