337 lines
10 KiB
Markdown
337 lines
10 KiB
Markdown
# Upload Workbench Redesign Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Rebuild the upload page into a responsive workbench layout that keeps the settings panel fully accessible while preserving the current upload, trim, subtitle-default, and TTS selection behavior.
|
|
|
|
**Architecture:** Keep the existing upload-page workflow and state model, but replace the rigid centered flex layout with a top-aligned page shell and responsive grid. Most of the work lives in `src/components/UploadScreen.tsx`, with a smaller shell adjustment in `src/App.tsx` and assertion updates in the upload-page tests. The implementation should stay behavior-compatible and lean on CSS/Tailwind layout changes instead of introducing new product state.
|
|
|
|
**Tech Stack:** React 19, TypeScript, Vite, Tailwind CSS v4 utilities, Lucide React icons, Vitest, React Testing Library
|
|
|
|
---
|
|
|
|
### Task 1: Lock the redesigned upload-page structure with failing tests
|
|
|
|
**Files:**
|
|
- Modify: `src/App.test.tsx`
|
|
- Modify: `src/components/UploadScreen.test.tsx`
|
|
- Reference: `src/App.tsx`
|
|
- Reference: `src/components/UploadScreen.tsx`
|
|
|
|
**Step 1: Write the failing test**
|
|
|
|
Add assertions that describe the redesigned shell and card structure before implementation:
|
|
|
|
```tsx
|
|
it('shows the upload workbench header and keeps the language switcher visible', () => {
|
|
render(<App />);
|
|
|
|
expect(screen.getByText('Video Translate')).toBeInTheDocument();
|
|
expect(
|
|
screen.getByText('Upload a source video, tune subtitle defaults, and choose dubbing settings before generation.'),
|
|
).toBeInTheDocument();
|
|
expect(screen.getByLabelText('switch-ui-language-zh')).toBeInTheDocument();
|
|
expect(screen.getByLabelText('switch-ui-language-en')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders upload, mode, subtitle defaults, and language cards inside the responsive workbench', () => {
|
|
renderUploadScreen();
|
|
|
|
expect(screen.getByTestId('upload-workbench')).toBeInTheDocument();
|
|
expect(screen.getByTestId('upload-dropzone-card')).toBeInTheDocument();
|
|
expect(screen.getByTestId('upload-settings-column')).toBeInTheDocument();
|
|
expect(screen.getByRole('heading', { name: 'Mode & Workflow' })).toBeInTheDocument();
|
|
expect(screen.getByRole('heading', { name: 'Subtitle Defaults' })).toBeInTheDocument();
|
|
expect(screen.getByRole('heading', { name: 'Language & Dubbing' })).toBeInTheDocument();
|
|
});
|
|
```
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx src/components/UploadScreen.test.tsx
|
|
```
|
|
|
|
Expected: FAIL because the new header copy, section headings, and test ids do not exist yet.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Do not redesign everything yet. Add only the smallest missing semantic hooks and copy needed to satisfy the new tests:
|
|
|
|
- app-shell heading and intro copy in `src/App.tsx`
|
|
- stable `data-testid` markers and section headings in `src/components/UploadScreen.tsx`
|
|
|
|
Use copy aligned with the approved design:
|
|
|
|
```tsx
|
|
<section aria-label="upload-page-intro">
|
|
<p>Video Translate</p>
|
|
<h1>{m.upload.workbenchTitle}</h1>
|
|
<p>{m.upload.workbenchDescription}</p>
|
|
</section>
|
|
```
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx src/components/UploadScreen.test.tsx
|
|
```
|
|
|
|
Expected: PASS for the new structure assertions, with no unrelated failures in those files.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/App.test.tsx src/components/UploadScreen.test.tsx src/App.tsx src/components/UploadScreen.tsx src/i18n.tsx
|
|
git commit -m "test: lock upload workbench shell structure"
|
|
```
|
|
|
|
### Task 2: Rebuild the page shell and header layout
|
|
|
|
**Files:**
|
|
- Modify: `src/App.tsx`
|
|
- Modify: `src/i18n.tsx`
|
|
- Test: `src/App.test.tsx`
|
|
|
|
**Step 1: Write the failing test**
|
|
|
|
Extend the app test to assert the header content is grouped into a page shell and remains bilingual:
|
|
|
|
```tsx
|
|
expect(screen.getByRole('banner')).toBeInTheDocument();
|
|
expect(screen.getByRole('heading', { name: 'Upload & prepare' })).toBeInTheDocument();
|
|
expect(screen.getByText('Keep subtitle defaults visible and avoid clipped settings panels.')).toBeInTheDocument();
|
|
```
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx
|
|
```
|
|
|
|
Expected: FAIL because the new banner structure and localized hero text are not present yet.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Update `src/App.tsx` and `src/i18n.tsx` to:
|
|
|
|
- wrap the upload page in a proper shell with `min-h-screen`
|
|
- add a responsive header above the view content
|
|
- move the language switcher into the header
|
|
- add localized upload-page title and description strings for both Chinese and English
|
|
|
|
Implementation sketch:
|
|
|
|
```tsx
|
|
<div className="min-h-screen bg-[...gradient...] text-slate-900">
|
|
<div className="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
|
|
<header className="mb-8 flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
|
<div className="space-y-3">
|
|
<p className="...">{m.app.productName}</p>
|
|
<h1 className="...">{m.upload.workbenchTitle}</h1>
|
|
<p className="...">{m.upload.workbenchDescription}</p>
|
|
</div>
|
|
{languageSwitcher}
|
|
</header>
|
|
{currentView === 'upload' ? <UploadScreen ... /> : <EditorScreen ... />}
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx
|
|
```
|
|
|
|
Expected: PASS, showing the new header shell in the upload view and preserving the language toggle behavior.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/App.tsx src/i18n.tsx src/App.test.tsx
|
|
git commit -m "feat: add upload page workbench shell"
|
|
```
|
|
|
|
### Task 3: Replace the rigid upload-page flex layout with a responsive workbench grid
|
|
|
|
**Files:**
|
|
- Modify: `src/components/UploadScreen.tsx`
|
|
- Modify: `src/components/UploadScreen.test.tsx`
|
|
- Modify: `src/index.css`
|
|
- Reference: `src/types.ts`
|
|
|
|
**Step 1: Write the failing test**
|
|
|
|
Add tests that lock in the redesigned composition while preserving behavior:
|
|
|
|
```tsx
|
|
it('keeps subtitle preview behavior after the layout redesign', () => {
|
|
renderUploadScreen();
|
|
|
|
fireEvent.change(screen.getByLabelText(/subtitle initial size/i), {
|
|
target: { value: '32' },
|
|
});
|
|
|
|
expect(screen.getByTestId('upload-subtitle-preview')).toHaveStyle({ fontSize: '32px' });
|
|
expect(screen.getByRole('button', { name: 'Generate Translated Video' })).toBeDisabled();
|
|
});
|
|
|
|
it('keeps the upload interaction wired through the redesigned dropzone card', () => {
|
|
renderUploadScreen();
|
|
|
|
expect(screen.getByTestId('upload-dropzone-card')).toContainElement(
|
|
screen.getByLabelText(/upload video file/i),
|
|
);
|
|
});
|
|
```
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
node ./node_modules/vitest/vitest.mjs run src/components/UploadScreen.test.tsx
|
|
```
|
|
|
|
Expected: FAIL because the current layout does not expose the new workbench structure markers and updated headings yet.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Refactor `src/components/UploadScreen.tsx` to:
|
|
|
|
- replace the outer `flex ... h-screen items-center` shell with a responsive grid container
|
|
- use a wide primary upload card plus a flexible sidebar column
|
|
- remove the fixed `w-[400px]` sidebar
|
|
- add `min-w-0` to shrinkable containers
|
|
- convert the settings area into three distinct cards:
|
|
- mode and workflow
|
|
- subtitle defaults
|
|
- language and dubbing
|
|
- reduce the preview height and tighten card spacing
|
|
- keep the language list internally scrollable
|
|
- keep the primary generation button prominent at the bottom of the settings flow
|
|
|
|
Representative JSX shape:
|
|
|
|
```tsx
|
|
<section
|
|
data-testid="upload-workbench"
|
|
className="grid gap-6 xl:grid-cols-[minmax(0,1.65fr)_minmax(320px,420px)]"
|
|
>
|
|
<div data-testid="upload-dropzone-card" className="min-w-0 ...">
|
|
...
|
|
</div>
|
|
|
|
<aside data-testid="upload-settings-column" className="min-w-0 space-y-5 xl:sticky xl:top-6">
|
|
<section className="...">
|
|
<h2>{m.upload.modeWorkflowTitle}</h2>
|
|
...
|
|
</section>
|
|
<section className="...">
|
|
<h2>{m.upload.subtitleDefaults}</h2>
|
|
...
|
|
</section>
|
|
<section className="...">
|
|
<h2>{m.upload.languageDubbingTitle}</h2>
|
|
...
|
|
</section>
|
|
</aside>
|
|
</section>
|
|
```
|
|
|
|
If a small amount of shared CSS improves consistency, keep it narrowly scoped in `src/index.css`:
|
|
|
|
```css
|
|
.app-surface {
|
|
background: rgba(255, 255, 255, 0.86);
|
|
backdrop-filter: blur(14px);
|
|
}
|
|
```
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
node ./node_modules/vitest/vitest.mjs run src/components/UploadScreen.test.tsx
|
|
```
|
|
|
|
Expected: PASS for the upload-screen interaction tests, including subtitle-preview updates and upload confirmation behavior.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/components/UploadScreen.tsx src/components/UploadScreen.test.tsx src/index.css src/i18n.tsx
|
|
git commit -m "feat: redesign upload page workbench layout"
|
|
```
|
|
|
|
### Task 4: Full verification and cleanup
|
|
|
|
**Files:**
|
|
- Modify: `src/App.tsx`
|
|
- Modify: `src/components/UploadScreen.tsx`
|
|
- Modify: `src/i18n.tsx`
|
|
- Modify: `src/index.css`
|
|
- Test: `src/App.test.tsx`
|
|
- Test: `src/components/UploadScreen.test.tsx`
|
|
|
|
**Step 1: Write the failing test**
|
|
|
|
Add one final regression assertion for the bilingual header text to ensure the redesigned shell still switches languages correctly:
|
|
|
|
```tsx
|
|
fireEvent.click(screen.getByLabelText('switch-ui-language-en'));
|
|
expect(screen.getByRole('heading', { name: 'Upload & prepare' })).toBeInTheDocument();
|
|
expect(screen.getByText('Upload a source video, tune subtitle defaults, and choose dubbing settings before generation.')).toBeInTheDocument();
|
|
```
|
|
|
|
**Step 2: Run test to verify it fails**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx
|
|
```
|
|
|
|
Expected: FAIL until the new localized header copy is fully wired.
|
|
|
|
**Step 3: Write minimal implementation**
|
|
|
|
Complete any remaining polish needed to satisfy the regression coverage:
|
|
|
|
- finalize localized header and section labels
|
|
- ensure the upload page stays visually coherent in both locales
|
|
- remove unused classes or stale layout wrappers from the old design
|
|
|
|
**Step 4: Run test to verify it passes**
|
|
|
|
Run:
|
|
|
|
```bash
|
|
node ./node_modules/vitest/vitest.mjs run src/App.test.tsx src/components/UploadScreen.test.tsx
|
|
npm run lint
|
|
npm run build
|
|
```
|
|
|
|
Expected:
|
|
|
|
- targeted tests PASS
|
|
- typecheck PASS
|
|
- production build PASS
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/App.tsx src/components/UploadScreen.tsx src/i18n.tsx src/index.css src/App.test.tsx src/components/UploadScreen.test.tsx
|
|
git commit -m "test: verify upload workbench redesign"
|
|
```
|