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 <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
d3bf13142b
commit
3923e20d8a
|
|
@ -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 (
|
||||||
|
<div className="h-full flex items-center justify-center bg-white/50">
|
||||||
|
<div className="text-center space-y-2">
|
||||||
|
<Film className="mx-auto w-12 h-12 text-gray-600" />
|
||||||
|
<div className="text-gray-700 font-semibold">Video upload coming in Phase 2</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const RightTop: React.FC = () => {
|
||||||
|
return <div className="h-full border rounded border-dashed border-gray-300" />
|
||||||
|
}
|
||||||
|
|
||||||
|
const BottomArea: React.FC = () => {
|
||||||
|
return <div className="border border-dashed border-gray-300 rounded h-full" />
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppLayout: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="h-screen grid grid-rows-[1fr_auto] grid-cols-2">
|
||||||
|
<div className="border-r border-b border-gray-200 p-4">
|
||||||
|
<VideoPlaceholder />
|
||||||
|
</div>
|
||||||
|
<div className="border-b border-gray-200 p-4">
|
||||||
|
<RightTop />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-2 p-4 border-t border-gray-200">
|
||||||
|
<BottomArea />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function App(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<AppLayout />
|
||||||
|
</QueryClientProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
)
|
||||||
|
|
@ -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(<App />)
|
||||||
|
const text = screen.getByText(/Video upload coming in Phase 2/i)
|
||||||
|
expect(text).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
import '@testing-library/jest-dom'
|
||||||
Loading…
Reference in New Issue