refactor(views): consistent loading and empty states

This commit is contained in:
Carlos Valente
2026-07-25 12:06:25 +00:00
parent f7535651f6
commit 94d54529ee
23 changed files with 256 additions and 206 deletions
@@ -2,7 +2,6 @@ import { MaybeString, ProjectRundown } from 'ontime-types';
import { memo, use, useMemo } from 'react';
import Select from '../../common/components/select/Select';
import EmptyPage from '../../common/components/state/EmptyPage';
import { PresetContext } from '../../common/context/PresetContext';
import useCustomFields from '../../common/hooks-query/useCustomFields';
import type { RundownSource } from '../../common/hooks-query/useScopedRundown';
@@ -34,40 +33,34 @@ function CuesheetTableWrapper({
const preset = use(PresetContext);
const isCurrentRundown = source.rundownId !== null && source.rundownId === loadedRundownId;
const { cuesheetMode, setCuesheetMode } = useApplyCuesheetPolicy(preset, { canRunMode: isCurrentRundown });
const { data: customFields, status: customFieldStatus } = useCustomFields();
const { data: customFields } = useCustomFields();
const columns = useMemo(
() => makeCuesheetColumns(customFields, cuesheetMode, preset),
[customFields, cuesheetMode, preset],
);
const isLoading = !customFields || customFieldStatus === 'pending';
return (
<CuesheetDnd columns={columns}>
{isLoading ? (
<EmptyPage text='Loading...' />
) : (
<CuesheetTable
columns={columns}
source={source}
cuesheetMode={cuesheetMode}
tableRoot='cuesheet'
setCuesheetMode={setCuesheetMode}
isCurrentRundown={isCurrentRundown}
insertElement={
<>
<RundownSelect
cuesheetMode={cuesheetMode}
selectedRundownId={selectedRundownId}
loadedRundownId={loadedRundownId}
setSelectedRundownId={setSelectedRundownId}
projectRundowns={projectRundowns}
/>
</>
}
/>
)}
<CuesheetTable
columns={columns}
source={source}
cuesheetMode={cuesheetMode}
tableRoot='cuesheet'
setCuesheetMode={setCuesheetMode}
isCurrentRundown={isCurrentRundown}
insertElement={
<>
<RundownSelect
cuesheetMode={cuesheetMode}
selectedRundownId={selectedRundownId}
loadedRundownId={loadedRundownId}
setSelectedRundownId={setSelectedRundownId}
projectRundowns={projectRundowns}
/>
</>
}
/>
</CuesheetDnd>
);
}
@@ -5,6 +5,10 @@ $table-header-font-size: calc(1rem - 2px);
@include rows.cuesheet-row-columns($table-header-font-size);
.tableLoading {
grid-area: table;
}
.cuesheet {
font-size: $table-font-size;
font-weight: 400;
@@ -11,7 +11,7 @@ import {
TableVirtuosoHandle,
} from 'react-virtuoso';
import EmptyPage from '../../../common/components/state/EmptyPage';
import EmptyFill from '../../../common/components/state/EmptyFill';
import EmptyTableBody from '../../../common/components/state/EmptyTableBody';
import { useEntryActionsContext } from '../../../common/context/EntryActionsContext';
import type { RundownSource } from '../../../common/hooks-query/useScopedRundown';
@@ -19,6 +19,7 @@ import type { ExtendedEntry } from '../../../common/utils/rundownMetadata';
import { usePersistedRundownOptions } from '../../../features/rundown/rundown.options';
import { useEventSelection } from '../../../features/rundown/useEventSelection';
import { AppMode } from '../../../ontimeConfig';
import { useTranslation } from '../../../translation/TranslationProvider';
import { usePersistedCuesheetOptions } from '../cuesheet.options';
import { useCuesheetPermissions } from '../useTablePermissions';
import { CuesheetHeader, SortableCuesheetHeader } from './cuesheet-table-elements/CuesheetHeader';
@@ -64,6 +65,7 @@ export default function CuesheetTable({
}: CuesheetTableProps) {
const { flatRundown, status, selectedEventId } = source;
const { updateEntry, updateTimer, addEntry } = useEntryActionsContext();
const { getLocalizedString } = useTranslation();
const canCreateEntries = useCuesheetPermissions((state) => state.canCreateEntries) && cuesheetMode === AppMode.Edit;
const useOptions = tableRoot === 'editor' ? usePersistedRundownOptions : usePersistedCuesheetOptions;
@@ -228,10 +230,13 @@ export default function CuesheetTable({
});
}, [cuesheetMode, hideIndexColumn, table]);
const isLoading = !flatRundown || status === 'pending';
// avoid showing the editable empty state before we know whether the rundown is actually empty
if (status === 'pending') {
return <EmptyFill text='Loading…' className={style.tableLoading} />;
}
if (isLoading) {
return <EmptyPage text='Loading...' />;
if (status === 'error') {
return <EmptyFill text={getLocalizedString('common.no_data')} className={style.tableLoading} />;
}
return (