From 3923e20d8addfa3230a70e9dc3d6a695ceb64534 Mon Sep 17 00:00:00 2001 From: Woody Date: Thu, 23 Apr 2026 10:58:03 +0800 Subject: [PATCH] feat(frontend): Phase 1.1 grid layout with Phase 2 pre-allocation and tests App layout uses CSS Grid with Top-Left video placeholder (Film icon), Top-Right empty container for Phase 1.2 input, and Bottom full-width container for Phase 1.2 response. Includes Layout test verifying Phase 2 placeholder text and API client tests verifying baseURL and mocked endpoint calls. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- frontend/src/App.tsx | 47 ++++++++++++++++++++ frontend/src/main.tsx | 11 +++++ frontend/src/test/components/Layout.test.tsx | 11 +++++ frontend/src/test/lib/api.test.ts | 23 ++++++++++ frontend/src/test/setup.ts | 1 + 5 files changed, 93 insertions(+) create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/test/components/Layout.test.tsx create mode 100644 frontend/src/test/lib/api.test.ts create mode 100644 frontend/src/test/setup.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..1080187 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,47 @@ +import React from 'react' +import { QueryClientProvider } from '@tanstack/react-query' +import { queryClient } from './lib/queries' +import { Film } from 'lucide-react' + +const VideoPlaceholder: React.FC = () => { + return ( +
+
+ +
Video upload coming in Phase 2
+
+
+ ) +} + +const RightTop: React.FC = () => { + return
+} + +const BottomArea: React.FC = () => { + return
+} + +const AppLayout: React.FC = () => { + return ( +
+
+ +
+
+ +
+
+ +
+
+ ) +} + +export default function App(): JSX.Element { + return ( + + + + ) +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..b83f6d0 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import { createRoot } from 'react-dom/client' +import App from './App' +import './styles.css' + +const root = createRoot(document.getElementById('root')!) +root.render( + + + +) diff --git a/frontend/src/test/components/Layout.test.tsx b/frontend/src/test/components/Layout.test.tsx new file mode 100644 index 0000000..ac9e532 --- /dev/null +++ b/frontend/src/test/components/Layout.test.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import App from '../../App' + +describe('Layout', () => { + test('renders VideoPlaceholder with Phase 2 text', () => { + render() + const text = screen.getByText(/Video upload coming in Phase 2/i) + expect(text).toBeInTheDocument() + }) +}) diff --git a/frontend/src/test/lib/api.test.ts b/frontend/src/test/lib/api.test.ts new file mode 100644 index 0000000..a64ee64 --- /dev/null +++ b/frontend/src/test/lib/api.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { apiClient, queryDocument, ingestDocument } from '../../lib/api' + +describe('API client basics', () => { + it('has a baseURL containing /api/v1', () => { + expect(apiClient.defaults.baseURL).toContain('/api/v1') + }) + + it('queryDocument posts and returns data', async () => { + const mockPost = vi.spyOn(apiClient, 'post') + mockPost.mockResolvedValueOnce({ data: { keywords: [], answer: '', sources: [] } }) + const res = await queryDocument({ question: 'test' }) + expect(res).toHaveProperty('answer') + }) + + it('ingestDocument posts and returns data', async () => { + const mockPost = vi.spyOn(apiClient, 'post') + mockPost.mockResolvedValueOnce({ data: { document_id: 'doc1', chunk_count: 1, filename: 'a.txt' } }) + const file = new File(['data'], 'a.txt', { type: 'text/plain' }) + const res = await ingestDocument(file) + expect(res).toHaveProperty('document_id') + }) +}) diff --git a/frontend/src/test/setup.ts b/frontend/src/test/setup.ts new file mode 100644 index 0000000..c44951a --- /dev/null +++ b/frontend/src/test/setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'