diff --git a/apps/client/src/common/components/state/EmptyTableBody.module.scss b/apps/client/src/common/components/state/EmptyTableBody.module.scss
index 678dd083f..b2288ffef 100644
--- a/apps/client/src/common/components/state/EmptyTableBody.module.scss
+++ b/apps/client/src/common/components/state/EmptyTableBody.module.scss
@@ -8,9 +8,12 @@
padding-top: 10vh;
}
- .empty {
- width: 100%;
- opacity: 0.8;
+ .inline {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 1rem;
+ margin-top: 1em;
}
.text {
diff --git a/apps/client/src/common/components/state/EmptyTableBody.tsx b/apps/client/src/common/components/state/EmptyTableBody.tsx
index 98a771d4f..0edd2a22c 100644
--- a/apps/client/src/common/components/state/EmptyTableBody.tsx
+++ b/apps/client/src/common/components/state/EmptyTableBody.tsx
@@ -1,18 +1,33 @@
-import EmptyImage from '../../../assets/images/empty.svg?react';
+import { SupportedEntry } from 'ontime-types';
+import { IoAdd } from 'react-icons/io5';
+
+import { useTranslation } from '../../../translation/TranslationProvider';
+import Button from '../buttons/Button';
+import Empty from './Empty';
import style from './EmptyTableBody.module.scss';
interface EmptyTableBodyProps {
- text: string;
+ handleAddNew?: (type: SupportedEntry) => void;
}
-export default function EmptyTableBody({ text }: EmptyTableBodyProps) {
+export default function EmptyTableBody({ handleAddNew }: EmptyTableBodyProps) {
+ const { getLocalizedString } = useTranslation();
+ const text = getLocalizedString('common.no_data');
return (
|
-
- {text && {text}}
+
+ {text}
+ {handleAddNew && (
+
+
+
+ )}
|
diff --git a/apps/client/src/features/app-settings/panel/manage-panel/ManageRundowns.tsx b/apps/client/src/features/app-settings/panel/manage-panel/ManageRundowns.tsx
index 3018cd916..0bc3e6c9b 100644
--- a/apps/client/src/features/app-settings/panel/manage-panel/ManageRundowns.tsx
+++ b/apps/client/src/features/app-settings/panel/manage-panel/ManageRundowns.tsx
@@ -157,7 +157,7 @@ export default function ManageRundowns() {
}
+ render={}
items={[
{
type: 'item',
diff --git a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx
index 89b155c68..32c3f60ac 100644
--- a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx
+++ b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx
@@ -1,6 +1,6 @@
import { useTableNav } from '@table-nav/react';
import { ColumnDef, Table, getCoreRowModel, useReactTable } from '@tanstack/react-table';
-import { OntimeEntry, TimeField, isOntimeDelay, isOntimeGroup, isOntimeMilestone } from 'ontime-types';
+import { OntimeEntry, SupportedEntry, TimeField, isOntimeDelay, isOntimeGroup, isOntimeMilestone } from 'ontime-types';
import { ComponentProps, ReactNode, memo, useCallback, useEffect, useMemo, useRef } from 'react';
import {
ContextProp,
@@ -20,6 +20,7 @@ import { usePersistedRundownOptions } from '../../../features/rundown/rundown.op
import { useEventSelection } from '../../../features/rundown/useEventSelection';
import { AppMode } from '../../../ontimeConfig';
import { usePersistedCuesheetOptions } from '../cuesheet.options';
+import { useCuesheetPermissions } from '../useTablePermissions';
import { CuesheetHeader, SortableCuesheetHeader } from './cuesheet-table-elements/CuesheetHeader';
import DelayRow from './cuesheet-table-elements/DelayRow';
import EventRow from './cuesheet-table-elements/EventRow';
@@ -62,7 +63,8 @@ export default function CuesheetTable({
insertElement,
}: CuesheetTableProps) {
const { flatRundown, status, selectedEventId } = source;
- const { updateEntry, updateTimer } = useEntryActionsContext();
+ const { updateEntry, updateTimer, addEntry } = useEntryActionsContext();
+ const canCreateEntries = useCuesheetPermissions((state) => state.canCreateEntries) && cuesheetMode === AppMode.Edit;
const useOptions = tableRoot === 'editor' ? usePersistedRundownOptions : usePersistedCuesheetOptions;
const optionsStore = useOptions();
@@ -202,8 +204,9 @@ export default function CuesheetTable({
listeners,
rows,
table,
+ handleAddNew: canCreateEntries ? (type: SupportedEntry) => addEntry({ type }) : undefined,
}),
- [columnSizeVars, cursor, listeners, rows, table],
+ [columnSizeVars, cursor, listeners, rows, table, addEntry, canCreateEntries],
);
const computeItemKey = useCallback((_: number, item: ExtendedEntry) => item.id, []);
@@ -273,10 +276,13 @@ interface CuesheetVirtuosoContext {
listeners: ReturnType['listeners'];
rows: ReturnType['getRowModel']>['rows'];
table: Table;
+ handleAddNew?: (type: SupportedEntry) => void;
}
-const EmptyPlaceholder = memo(function EmptyPlaceholder() {
- return ;
+const EmptyPlaceholder = memo(function EmptyPlaceholder({
+ context,
+}: TableProps & ContextProp) {
+ return ;
});
const CuesheetTableElement = memo(function CuesheetTableElement({
diff --git a/e2e/tests/features/202-cuesheet.spec.ts b/e2e/tests/features/202-cuesheet.spec.ts
index bb186c6df..a40112136 100644
--- a/e2e/tests/features/202-cuesheet.spec.ts
+++ b/e2e/tests/features/202-cuesheet.spec.ts
@@ -108,3 +108,27 @@ test('cuesheet datagrid keeps keyboard focus flow while editing text cells', asy
await expect(cueCell).toContainText(cueBeforeCancel);
await expect(cueCell).toBeFocused();
});
+
+test('cuesheet background edit from empty state', async ({ page }) => {
+ // create an empty rundown
+ await page.goto('/editor');
+ await page.getByRole('button', { name: 'Toggle settings' }).click();
+ await page.getByRole('button', { name: 'Manage rundowns' }).click();
+ await page.getByRole('button', { name: 'New' }).nth(1).click();
+ const emptyName = `empty-${Date.now()}`;
+ await page.getByRole('textbox', { name: 'Rundown title' }).fill(emptyName);
+ await page.getByRole('button', { name: 'Create rundown' }).click();
+
+ // edit it in the cuesheet
+ await page.getByRole('row', { name: '0 empty-' }).getByTestId('rundown_menu').click();
+ await page.getByText('Edit in cuesheet').click();
+
+ // expect to see and empty screen
+ await expect(page.getByRole('button', { name: 'Create Event' })).toBeVisible();
+
+ // create 1 event
+ await page.getByRole('button', { name: 'Create Event' }).click();
+
+ // and expect to find it
+ await expect(page.getByTestId('cuesheet-event')).toBeVisible();
+});
|