refactor(finder): flatten the result type and trim the tests

The three-variant result union differed only in whether an entry had a
cue, an event index and a parent, and the UI narrowed it with runtime
property checks anyway, so the discrimination bought nothing while
costing three near-identical branches to build a result. One flat type
removes those branches and the checks around them.

Skipping non-searchable entries asked whether an entry was a group or a
milestone, then asked again when building the result. It now asks once
whether it is a delay, which is what the guard actually meant.

The search functions move out of the memo closure to the module, where
they are ordinary pure functions.

Whether to show the matched field compared against its display label, so
a custom field labelled "Title" would have had its match hidden. It now
compares the field key.

Drops the review document, which had served its purpose, and reduces the
finder tests to a single one covering the path a user takes: open from a
focused field, find an entry by its note, scope with a badge, and reveal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DzALEq9gGWwFmwTdgAiFcY
This commit is contained in:
Claude
2026-08-18 18:59:29 +00:00
parent 2fc072a9d3
commit bb221685ea
4 changed files with 170 additions and 800 deletions
@@ -1,10 +1,10 @@
import { EntryId, MaybeString, SupportedEntry } from 'ontime-types';
import { EntryId, MaybeString } from 'ontime-types';
import { KeyboardEvent, PointerEvent, useDeferredValue, useEffect, useRef, useState } from 'react';
import Input from '../../../common/components/input/input/Input';
import Kbd from '../../../common/components/kbd/Kbd';
import Modal from '../../../common/components/modal/Modal';
import useFinder, { FilterableEntry } from './useFinder';
import useFinder, { FinderResult } from './useFinder';
import style from './Finder.module.scss';
@@ -69,7 +69,7 @@ export default function Finder({ isOpen, onClose }: FinderProps) {
}
};
const submit = (entry: FilterableEntry | undefined) => {
const submit = (entry: FinderResult | undefined) => {
if (!entry) {
return;
}
@@ -132,10 +132,8 @@ export default function Finder({ isOpen, onClose }: FinderProps) {
{!error && results.length === 0 && <li className={style.empty}>No results</li>}
{results.map((entry) => {
const isSelected = activeEntry?.id === entry.id;
const displayIndex = entry.type === SupportedEntry.Event ? entry.eventIndex : '-';
const displayCue = 'cue' in entry ? entry.cue : '';
// the title and cue are already on the row, anything else needs showing
const showMatch = entry.match !== null && entry.match.label !== 'Title' && entry.match.label !== 'Cue';
// the title and cue are already on the row, a match anywhere else needs showing
const showMatch = entry.match !== null && entry.match.key !== 'title' && entry.match.key !== 'cue';
return (
<li
@@ -149,9 +147,9 @@ export default function Finder({ isOpen, onClose }: FinderProps) {
>
<div className={style.data}>
<div className={style.index} style={{ '--color': entry.colour }}>
{displayIndex}
{entry.eventIndex ?? '-'}
</div>
<div className={style.cue}>{displayCue}</div>
<div className={style.cue}>{entry.cue}</div>
<div className={style.title}>{entry.title}</div>
{showMatch && (
<div className={style.match} data-testid='finder-result-match'>