@robosystems/core 0.5.0 → 0.5.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robosystems/core",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Shared RoboSystems frontend core library (auth, contexts, task monitoring, UI components) for the RoboSystems Next.js apps",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -0,0 +1,19 @@
1
+ import type { CoverageItem } from './types';
2
+ export interface CoverageBrowserProps {
3
+ items: CoverageItem[];
4
+ /** Base path for card links, forwarded to the grid (defaults to /research). */
5
+ hrefBase?: string;
6
+ /** Override the search box placeholder. */
7
+ placeholder?: string;
8
+ }
9
+ /**
10
+ * The research listing with a live client-side filter above it. A drop-in
11
+ * replacement for {@link CoverageGrid}: the full grid renders on first paint
12
+ * (so the server-rendered SEO page keeps every card in its HTML), and typing
13
+ * narrows it in the browser. Matches ticker, company, title, and tags —
14
+ * someone who types "Green Thumb" finds GTBIF without knowing the symbol.
15
+ *
16
+ * Filtering the whole in-memory catalog is fine at this scale; when the
17
+ * catalog outgrows a single fetch this is where server-backed search slots in.
18
+ */
19
+ export declare function CoverageBrowser({ items, hrefBase, placeholder, }: CoverageBrowserProps): import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { TextInput } from 'flowbite-react';
4
+ import { useMemo, useState } from 'react';
5
+ import { HiSearch } from 'react-icons/hi';
6
+ import { CoverageGrid } from './CoverageGrid';
7
+ /**
8
+ * The research listing with a live client-side filter above it. A drop-in
9
+ * replacement for {@link CoverageGrid}: the full grid renders on first paint
10
+ * (so the server-rendered SEO page keeps every card in its HTML), and typing
11
+ * narrows it in the browser. Matches ticker, company, title, and tags —
12
+ * someone who types "Green Thumb" finds GTBIF without knowing the symbol.
13
+ *
14
+ * Filtering the whole in-memory catalog is fine at this scale; when the
15
+ * catalog outgrows a single fetch this is where server-backed search slots in.
16
+ */
17
+ export function CoverageBrowser({ items, hrefBase, placeholder = 'Search by ticker, company, or title…', }) {
18
+ const [query, setQuery] = useState('');
19
+ const filtered = useMemo(() => {
20
+ const term = query.trim().toLowerCase();
21
+ if (!term)
22
+ return items;
23
+ return items.filter((item) => item.ticker.toLowerCase().includes(term) ||
24
+ item.company.toLowerCase().includes(term) ||
25
+ item.title.toLowerCase().includes(term) ||
26
+ item.tags.some((tag) => tag.toLowerCase().includes(term)));
27
+ }, [items, query]);
28
+ // Empty catalog: let the grid render its own "no coverage yet" state, no
29
+ // point showing a search box over nothing.
30
+ if (!items.length) {
31
+ return _jsx(CoverageGrid, { items: items, hrefBase: hrefBase });
32
+ }
33
+ const term = query.trim();
34
+ return (_jsxs("div", { className: "space-y-6", children: [_jsx(TextInput, { icon: HiSearch, type: "search", value: query, onChange: (e) => setQuery(e.target.value), placeholder: placeholder, "aria-label": "Search research" }), term && filtered.length === 0 ? (_jsxs("p", { className: "py-12 text-center text-gray-500 dark:text-gray-400", children: ["No results for \u201C", term, "\u201D."] })) : (_jsx(CoverageGrid, { items: filtered, hrefBase: hrefBase }))] }));
35
+ }
@@ -1,4 +1,5 @@
1
1
  export { CATALOG_URL, fetchBrief, fetchCatalog, getAllCoverage, getCoverage, getCoverageTickers, youtubeId, } from './catalog';
2
+ export { CoverageBrowser, type CoverageBrowserProps } from './CoverageBrowser';
2
3
  export { CoverageCard } from './CoverageCard';
3
4
  export { CoverageGrid } from './CoverageGrid';
4
5
  export { CoverageHistory } from './CoverageHistory';
package/research/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // roboinvestor-app (logged-in). Data layer reads the S3 catalog produced by
3
3
  // robosystems-content-machine; components render the coverage + continuing-coverage thread.
4
4
  export { CATALOG_URL, fetchBrief, fetchCatalog, getAllCoverage, getCoverage, getCoverageTickers, youtubeId, } from './catalog';
5
+ export { CoverageBrowser } from './CoverageBrowser';
5
6
  export { CoverageCard } from './CoverageCard';
6
7
  export { CoverageGrid } from './CoverageGrid';
7
8
  export { CoverageHistory } from './CoverageHistory';
@@ -30,5 +30,5 @@ export function LandingFooter({ tagline, productLinks, contactModal: ContactModa
30
30
  return (_jsx("li", { children: _jsx(Link, Object.assign({ href: cfg.url }, (isCurrent
31
31
  ? {}
32
32
  : { target: '_blank', rel: 'noopener noreferrer' }), { className: linkClass, children: cfg.displayName })) }, app));
33
- }) })] }), _jsxs("div", { children: [_jsx("h3", { className: headingClass, children: "Company" }), _jsxs("ul", { className: "space-y-2 text-sm", children: [_jsx("li", { children: _jsx(Link, Object.assign({ href: `${companyBase}/research` }, companyLinkProps, { className: linkClass, children: "Research" })) }), _jsx("li", { children: _jsx(Link, Object.assign({ href: `${companyBase}/blog` }, companyLinkProps, { className: linkClass, children: "Blog" })) }), _jsx("li", { children: _jsx(Link, Object.assign({ href: `${companyBase}/pages/privacy` }, companyLinkProps, { className: linkClass, children: "Privacy" })) }), _jsx("li", { children: _jsx(Link, Object.assign({ href: `${companyBase}/pages/terms` }, companyLinkProps, { className: linkClass, children: "Terms" })) }), ContactModal && (_jsx("li", { children: _jsx("button", { onClick: () => setShowContact(true), className: linkClass, children: "Contact" }) }))] })] })] }), _jsx("div", { className: "mt-8 border-t border-gray-800 pt-8 text-center text-sm text-gray-400", children: _jsxs("p", { children: ["\u00A9 ", new Date().getFullYear(), " RFS LLC. All rights reserved."] }) })] }), ContactModal && (_jsx(ContactModal, { isOpen: showContact, onClose: () => setShowContact(false) }))] }));
33
+ }) })] }), _jsxs("div", { children: [_jsx("h3", { className: headingClass, children: "Company" }), _jsxs("ul", { className: "space-y-2 text-sm", children: [_jsx("li", { children: _jsx(Link, Object.assign({ href: `${companyBase}/research` }, companyLinkProps, { className: linkClass, children: "Research" })) }), _jsx("li", { children: _jsx(Link, { href: "https://holon.robosystems.ai", target: "_blank", rel: "noopener noreferrer", className: linkClass, children: "Holon Viewer" }) }), _jsx("li", { children: _jsx(Link, Object.assign({ href: `${companyBase}/blog` }, companyLinkProps, { className: linkClass, children: "Blog" })) }), _jsx("li", { children: _jsx(Link, Object.assign({ href: `${companyBase}/pages/privacy` }, companyLinkProps, { className: linkClass, children: "Privacy" })) }), _jsx("li", { children: _jsx(Link, Object.assign({ href: `${companyBase}/pages/terms` }, companyLinkProps, { className: linkClass, children: "Terms" })) }), ContactModal && (_jsx("li", { children: _jsx("button", { onClick: () => setShowContact(true), className: linkClass, children: "Contact" }) }))] })] })] }), _jsx("div", { className: "mt-8 border-t border-gray-800 pt-8 text-center text-sm text-gray-400", children: _jsxs("p", { children: ["\u00A9 ", new Date().getFullYear(), " RFS LLC. All rights reserved."] }) })] }), ContactModal && (_jsx(ContactModal, { isOpen: showContact, onClose: () => setShowContact(false) }))] }));
34
34
  }