feat: allow finding milestones

This commit is contained in:
Carlos Valente
2025-09-09 20:25:25 +02:00
committed by Carlos Valente
parent ded8bddb2d
commit e4b0df42cf
3 changed files with 49 additions and 25 deletions
@@ -66,4 +66,5 @@
.scrollContainer { .scrollContainer {
max-height: 70vh; max-height: 70vh;
overflow: auto; overflow: auto;
padding-top: 1rem;
} }
@@ -71,8 +71,7 @@ export default function Finder({ isOpen, onClose }: FinderProps) {
results.map((entry, index) => { results.map((entry, index) => {
const isSelected = selected === index; const isSelected = selected === index;
const displayIndex = entry.type === SupportedEntry.Event ? entry.eventIndex : '-'; const displayIndex = entry.type === SupportedEntry.Event ? entry.eventIndex : '-';
const displayCue = entry.type === SupportedEntry.Event ? entry.cue : ''; const displayCue = 'cue' in entry ? entry.cue : '';
const colour = entry.type === SupportedEntry.Event ? entry.colour : '';
return ( return (
<li <li
@@ -83,7 +82,7 @@ export default function Finder({ isOpen, onClose }: FinderProps) {
onClick={submit} onClick={submit}
> >
<div className={style.data}> <div className={style.data}>
<div className={style.index} style={{ '--color': colour }}> <div className={style.index} style={{ '--color': entry.colour }}>
{displayIndex} {displayIndex}
</div> </div>
<div className={style.cue}>{displayCue}</div> <div className={style.cue}>{displayCue}</div>
@@ -99,7 +98,7 @@ export default function Finder({ isOpen, onClose }: FinderProps) {
footerElements={ footerElements={
<div className={style.footer}> <div className={style.footer}>
Use the keywords <span className={style.em}>cue</span>, <span className={style.em}>index</span> or Use the keywords <span className={style.em}>cue</span>, <span className={style.em}>index</span> or
<span className={style.em}>title</span> to filter search <span className={style.em}>title</span> to filter search.
</div> </div>
} }
/> />
@@ -1,6 +1,6 @@
import { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react'; import { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react';
import { useSessionStorage } from '@mantine/hooks'; import { useSessionStorage } from '@mantine/hooks';
import { EntryId, isOntimeEvent, isOntimeGroup, MaybeString, SupportedEntry } from 'ontime-types'; import { EntryId, isOntimeEvent, isOntimeGroup, isOntimeMilestone, MaybeString, SupportedEntry } from 'ontime-types';
import { useFlatRundown } from '../../../common/hooks-query/useRundown'; import { useFlatRundown } from '../../../common/hooks-query/useRundown';
import { useEventSelection } from '../../../features/rundown/useEventSelection'; import { useEventSelection } from '../../../features/rundown/useEventSelection';
@@ -9,14 +9,15 @@ const maxResults = 12;
type FilterableGroup = { type FilterableGroup = {
type: SupportedEntry.Group; type: SupportedEntry.Group;
id: string; id: EntryId;
index: number; index: number;
title: string; title: string;
colour: string;
}; };
type FilterableEvent = { type FilterableEvent = {
type: SupportedEntry.Event; type: SupportedEntry.Event;
id: string; id: EntryId;
index: number; index: number;
eventIndex: number; eventIndex: number;
title: string; title: string;
@@ -25,7 +26,17 @@ type FilterableEvent = {
parent: MaybeString; parent: MaybeString;
}; };
type FilterableEntry = FilterableGroup | FilterableEvent; type FilterableMilestone = {
type: SupportedEntry.Milestone;
id: EntryId;
index: number;
title: string;
cue: string;
colour: string;
parent: MaybeString;
};
type FilterableEntry = FilterableGroup | FilterableEvent | FilterableMilestone;
export default function useFinder() { export default function useFinder() {
const { data, rundownId } = useFlatRundown(); const { data, rundownId } = useFlatRundown();
@@ -59,7 +70,7 @@ export default function useFinder() {
lastSearchString.current = searchValue; lastSearchString.current = searchValue;
if (searchValue.startsWith('index ')) { if (searchValue.startsWith('index ')) {
const searchString = searchValue.replace('index ', '').trim(); const searchString = searchValue.slice('index '.length).trim();
const { results, error } = searchByIndex(searchString); const { results, error } = searchByIndex(searchString);
setResults(results); setResults(results);
setError(error); setError(error);
@@ -67,14 +78,14 @@ export default function useFinder() {
} }
if (searchValue.startsWith('cue ')) { if (searchValue.startsWith('cue ')) {
const searchString = searchValue.replace('cue ', '').trim(); const searchString = searchValue.slice('cue '.length).trim();
const { results, error } = searchByCue(searchString); const { results, error } = searchByCue(searchString);
setResults(results); setResults(results);
setError(error); setError(error);
return; return;
} }
const searchString = searchValue.replace('title ', '').trim(); const searchString = searchValue.startsWith('title ') ? searchValue.slice('title '.length).trim() : searchValue;
const { results, error } = searchByTitle(searchString); const { results, error } = searchByTitle(searchString);
setResults(results); setResults(results);
setError(error); setError(error);
@@ -162,33 +173,46 @@ export default function useFinder() {
break; break;
} }
const event = data[i]; const entry = data[i];
if (isOntimeEvent(event)) { if (isOntimeEvent(entry)) {
if (event.title.toLowerCase().includes(searchString)) { if (entry.title.toLowerCase().includes(searchString)) {
remaining--; remaining--;
results.push({ results.push({
type: SupportedEntry.Event, type: SupportedEntry.Event,
id: event.id, id: entry.id,
index: i, index: i,
eventIndex, eventIndex,
title: event.title, title: entry.title,
cue: event.cue, cue: entry.cue,
colour: event.colour, colour: entry.colour,
parent: event.parent, parent: entry.parent,
} satisfies FilterableEvent); } satisfies FilterableEvent);
} }
eventIndex++; eventIndex++;
} } else if (isOntimeGroup(entry)) {
if (isOntimeGroup(event)) { if (entry.title.toLowerCase().includes(searchString)) {
if (event.title.toLowerCase().includes(searchString)) {
remaining--; remaining--;
results.push({ results.push({
type: SupportedEntry.Group, type: SupportedEntry.Group,
id: event.id, id: entry.id,
index: i, index: i,
title: event.title, title: entry.title,
colour: entry.colour,
} satisfies FilterableGroup); } satisfies FilterableGroup);
} }
} else if (isOntimeMilestone(entry)) {
if (entry.title.toLowerCase().includes(searchString)) {
remaining--;
results.push({
type: SupportedEntry.Milestone,
id: entry.id,
index: i,
title: entry.title,
cue: entry.cue,
colour: entry.colour,
parent: entry.parent,
} satisfies FilterableMilestone);
}
} }
} }
return { results, error: null }; return { results, error: null };
@@ -200,7 +224,7 @@ export default function useFinder() {
const select = useCallback( const select = useCallback(
(selectedEvent: FilterableEntry) => { (selectedEvent: FilterableEntry) => {
// First expand the parent group if this is an event inside a group // First expand the parent group if this is an event inside a group
if (selectedEvent.type === SupportedEntry.Event && selectedEvent.parent !== null) { if ('parent' in selectedEvent && selectedEvent.parent !== null) {
// Try direct state update instead of using callback // Try direct state update instead of using callback
const currentGroups = [...new Set(collapsedGroups)]; const currentGroups = [...new Set(collapsedGroups)];
const newGroups = currentGroups.filter((id) => id !== selectedEvent.parent); const newGroups = currentGroups.filter((id) => id !== selectedEvent.parent);