From b446a6d9cf1e9971b4a3f34f3ee45bedaed2489a Mon Sep 17 00:00:00 2001
From: RazorCrest00 <146749797+RazorCrest00@users.noreply.github.com>
Date: Tue, 28 Jul 2026 12:48:43 -0700
Subject: [PATCH] [bugfix]: distinguish dataset loading errors from empty lists
---
.../src/app/datasets/page.test.tsx | 64 +++++++++++++
.../src/app/datasets/page.tsx | 91 +++++++++++++++----
2 files changed, 135 insertions(+), 20 deletions(-)
create mode 100644 apps/fastvideo_studio/src/app/datasets/page.test.tsx
diff --git a/apps/fastvideo_studio/src/app/datasets/page.test.tsx b/apps/fastvideo_studio/src/app/datasets/page.test.tsx
new file mode 100644
index 000000000..3245eba09
--- /dev/null
+++ b/apps/fastvideo_studio/src/app/datasets/page.test.tsx
@@ -0,0 +1,64 @@
+import { act, fireEvent, render, screen } from '@testing-library/react';
+import { describe, expect, it, vi } from 'vitest';
+
+import { HeaderActionsProvider } from '@/components/shell/HeaderActionsContext';
+import { getDatasets, type Dataset } from '@/lib/api';
+
+import DatasetsPage from './page';
+
+vi.mock('@/lib/api', () => ({
+ getDatasets: vi.fn(),
+}));
+
+vi.mock('@/components/datasets/AddDatasetButton', () => ({
+ default: () => null,
+}));
+
+vi.mock('@/components/datasets/CreateDatasetModal', () => ({
+ default: () => null,
+}));
+
+vi.mock('@/components/datasets/DatasetCard', () => ({
+ default: ({ dataset }: { dataset: Dataset }) =>
{dataset.name}
,
+}));
+
+function renderPage() {
+ return render(
+
+
+ ,
+ );
+}
+
+describe('DatasetsPage', () => {
+ it('shows loading content before the initial request settles', async () => {
+ let resolveDatasets: (datasets: Dataset[]) => void = () => {};
+ vi.mocked(getDatasets).mockReturnValue(
+ new Promise((resolve) => {
+ resolveDatasets = resolve;
+ }),
+ );
+
+ renderPage();
+ expect(screen.getByLabelText('Loading datasets')).toBeInTheDocument();
+ expect(screen.queryByText('No datasets yet.')).not.toBeInTheDocument();
+
+ act(() => resolveDatasets([]));
+ expect(await screen.findByText('No datasets yet.')).toBeInTheDocument();
+ });
+
+ it('shows API failures separately from an empty list and retries', async () => {
+ vi.spyOn(console, 'error').mockImplementation(() => {});
+ vi.mocked(getDatasets).mockRejectedValueOnce(new Error('network down'));
+
+ renderPage();
+ expect(
+ await screen.findByText(/Could not load datasets from the Studio API/),
+ ).toBeInTheDocument();
+ expect(screen.queryByText('No datasets yet.')).not.toBeInTheDocument();
+
+ vi.mocked(getDatasets).mockResolvedValueOnce([]);
+ fireEvent.click(screen.getByRole('button', { name: 'Try Again' }));
+ expect(await screen.findByText('No datasets yet.')).toBeInTheDocument();
+ });
+});
diff --git a/apps/fastvideo_studio/src/app/datasets/page.tsx b/apps/fastvideo_studio/src/app/datasets/page.tsx
index 2db891113..70458edbf 100644
--- a/apps/fastvideo_studio/src/app/datasets/page.tsx
+++ b/apps/fastvideo_studio/src/app/datasets/page.tsx
@@ -1,12 +1,14 @@
'use client';
import * as React from 'react';
+import { AlertTriangle } from 'lucide-react';
import AddDatasetButton from '@/components/datasets/AddDatasetButton';
import CreateDatasetModal from '@/components/datasets/CreateDatasetModal';
import DatasetCard from '@/components/datasets/DatasetCard';
import { HeaderActions } from '@/components/shell/HeaderActionsContext';
import { Card } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
import { useStore } from '@/hooks/useStore';
import { getDatasets } from '@/lib/api';
import type { Dataset } from '@/lib/api';
@@ -21,18 +23,28 @@ import {
export default function DatasetsPage() {
const [datasets, setDatasets] = React.useState([]);
+ const [isInitialLoading, setIsInitialLoading] = React.useState(true);
const [error, setError] = React.useState(null);
const { open } = useStore(createDatasetModalStore);
+ const fetchSequence = React.useRef(0);
const fetchDatasets = React.useCallback(async () => {
+ const sequence = ++fetchSequence.current;
try {
- setDatasets(await getDatasets());
- setError(null);
+ const next = await getDatasets();
+ if (sequence === fetchSequence.current) {
+ setDatasets(next);
+ setError(null);
+ }
} catch (err) {
console.error('Failed to fetch datasets:', err);
- // Distinguish an API outage from a genuinely empty list, so the user
- // isn't told they have no datasets when the server is unreachable.
- setError(err instanceof Error ? err.message : 'Failed to load datasets');
+ if (sequence === fetchSequence.current) {
+ setError(
+ 'Could not load datasets from the Studio API. Check the server and try again.',
+ );
+ }
+ } finally {
+ if (sequence === fetchSequence.current) setIsInitialLoading(false);
}
}, []);
@@ -52,22 +64,61 @@ export default function DatasetsPage() {
-
- {error ? (
-
{error}
- ) : datasets.length === 0 ? (
-
- No datasets yet.
-
- ) : (
- datasets.map((ds) => (
-
handleSelectDataset(ds)}
+
+ {isInitialLoading ? (
+
+ {[0, 1, 2].map((item) => (
+
+ ))}
+
+ ) : error && datasets.length === 0 ? (
+
+
- ))
+
+ {error}
+
+
+
+ ) : (
+ <>
+ {error && (
+
+ Dataset updates are temporarily unavailable. Showing the
+ most recent results.
+
+ )}
+ {datasets.length === 0 ? (
+
+ No datasets yet.
+
+ ) : (
+ datasets.map((ds) => (
+
handleSelectDataset(ds)}
+ />
+ ))
+ )}
+ >
)}