diff --git a/demos/product-docs-website/IMPLEMENTATION_PLAN.md b/demos/product-docs-website/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..d31f9e6 --- /dev/null +++ b/demos/product-docs-website/IMPLEMENTATION_PLAN.md @@ -0,0 +1,745 @@ +# Technical Implementation Plan: Product Documentation Website + +## Executive Summary + +This document outlines a test-driven development (TDD) approach to building a simple, responsive website that lists and provides access to Medtronic product documentation stored in Google Drive. The website will display product documentation for cardiac devices including the Micra leadless pacemaker family and other medical devices. + +## Product Documentation Inventory + +Based on the Google Drive folder analysis, the following product documentation is available: + +### Micra Leadless Pacemaker Family +- `01_Micra VR2 technical specifications.pdf` +- `02_Micra VR2 spec sheet (Western Europe).pdf` +- `02_Micra AV2 technical specifications.pdf` +- `03_Micra AV MC1AVR1 implant manual with MRI and EMI precautions.pdf` +- `04_Micra portfolio brochure (AV2 and VR2).pdf` +- `04_Living with the Micra leadless pacemaker.pdf` +- `01_Micra AV2 product page.html` +- `03_Micra AV2 and VR2 product page (EMEA).html` + +### Other Cardiac Products +- `01_Coronary product catalog.pdf` +- `02_Symplicity Spyral brochure.pdf` + +### Additional Resources +- `products.csv` - Product catalog with model numbers, pricing, and specifications +- `DATA_DICTIONARY.md` - Data dictionary for synthetic Medtronic data + +**Total Documents**: 12+ PDFs, HTML files, and data files + +## Technology Stack + +### Frontend +- **Framework**: React 18+ with TypeScript +- **Build Tool**: Vite +- **Styling**: Tailwind CSS +- **Component Library**: shadcn/ui (optional, for polished UI components) +- **Icons**: Lucide React + +### Testing +- **Unit/Integration Tests**: Vitest + React Testing Library +- **E2E Tests**: Playwright +- **Test Coverage**: nyc/c8 + +### Data Source Integration +- **API Layer**: Express.js or Next.js API Routes +- **Google Drive API**: Google Drive API v3 for programmatic access +- **Authentication**: Service Account or OAuth 2.0 + +### Deployment +- **Hosting**: Vercel, Netlify, or GitHub Pages +- **CI/CD**: GitHub Actions + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────────┐ +│ Client (Browser) │ +│ ┌───────────────────────────────────────────────────┐ │ +│ │ React App │ │ +│ │ - Product List Component │ │ +│ │ - Search/Filter Component │ │ +│ │ - Document Viewer Component │ │ +│ │ - Category Navigation │ │ +│ └───────────────────────────────────────────────────┘ │ +└─────────────────────┬───────────────────────────────────┘ + │ HTTP/REST + ▼ +┌─────────────────────────────────────────────────────────┐ +│ API Layer │ +│ ┌───────────────────────────────────────────────────┐ │ +│ │ /api/documents - List all documents │ │ +│ │ /api/documents/:id - Get document details │ │ +│ │ /api/categories - Get document categories │ │ +│ │ /api/search?q=query - Search documents │ │ +│ └───────────────────────────────────────────────────┘ │ +└─────────────────────┬───────────────────────────────────┘ + │ Google Drive API + ▼ +┌─────────────────────────────────────────────────────────┐ +│ Google Drive Folder │ +│ folder: 1KuN_YhLOk4KTdORa0rdpG24hLyd4kjfP │ +│ - PDF Documents │ +│ - HTML Resources │ +│ - CSV/Markdown Data Files │ +└─────────────────────────────────────────────────────────┘ +``` + +## Data Model + +### Document Entity +```typescript +interface ProductDocument { + id: string; // Google Drive file ID + name: string; // Display name + fileName: string; // Original filename + category: DocumentCategory; // Product category + fileType: 'pdf' | 'html' | 'csv' | 'markdown'; + size: number; // File size in bytes + lastModified: Date; // Last modification date + thumbnailUrl?: string; // Optional thumbnail + downloadUrl: string; // Direct download link + viewUrl: string; // Google Drive viewer URL + description?: string; // Optional description + productFamily?: string; // e.g., "Micra", "Symplicity" + modelNumber?: string; // e.g., "MC1VR2", "MC2AVR1" +} + +enum DocumentCategory { + TECHNICAL_SPECS = 'Technical Specifications', + USER_MANUAL = 'User Manual', + PRODUCT_BROCHURE = 'Product Brochure', + PATIENT_EDUCATION = 'Patient Education', + CATALOG = 'Product Catalog', + DATA_REFERENCE = 'Data Reference' +} +``` + +## TDD Implementation Phases + +### Phase 1: Project Setup & Infrastructure (Week 1) + +#### Test Cases +1. **Test**: Project builds successfully with TypeScript +2. **Test**: ESLint configuration passes with no errors +3. **Test**: Prettier formats code consistently +4. **Test**: Basic Vitest setup runs and passes smoke test + +#### Implementation +```bash +# Initialize project +npm create vite@latest product-docs-site -- --template react-ts +cd product-docs-site +npm install + +# Install dependencies +npm install -D vitest @testing-library/react @testing-library/jest-dom +npm install -D @playwright/test +npm install -D tailwindcss postcss autoprefixer +npm install lucide-react +npm install axios + +# Setup Tailwind +npx tailwindcss init -p +``` + +#### Configuration Files +- `vitest.config.ts` - Vitest configuration +- `playwright.config.ts` - E2E test configuration +- `tsconfig.json` - TypeScript strict mode +- `.eslintrc.cjs` - Linting rules +- `.prettierrc` - Code formatting + +### Phase 2: Google Drive API Integration (Week 1-2) + +#### Test Cases +1. **Test**: `fetchDocuments()` returns array of documents +2. **Test**: `fetchDocuments()` handles API errors gracefully +3. **Test**: Document metadata is correctly parsed +4. **Test**: File type detection works for PDF, HTML, CSV +5. **Test**: Rate limiting is respected (mock API) +6. **Test**: Authentication credentials are validated + +#### Implementation +```typescript +// src/services/googleDrive.service.ts +import { google } from 'googleapis'; + +export class GoogleDriveService { + private drive; + + constructor(apiKey: string) { + this.drive = google.drive({ version: 'v3', auth: apiKey }); + } + + async fetchDocuments(folderId: string): Promise { + // Implementation + } + + async getDocumentMetadata(fileId: string): Promise { + // Implementation + } +} +``` + +#### Test File +```typescript +// src/services/__tests__/googleDrive.service.test.ts +describe('GoogleDriveService', () => { + it('should fetch documents from folder', async () => { + const service = new GoogleDriveService(mockApiKey); + const docs = await service.fetchDocuments('test-folder-id'); + expect(docs).toHaveLength(12); + }); + + it('should categorize Micra documents correctly', async () => { + const service = new GoogleDriveService(mockApiKey); + const docs = await service.fetchDocuments('test-folder-id'); + const microDocs = docs.filter(d => d.productFamily === 'Micra'); + expect(microDocs.length).toBeGreaterThan(0); + }); +}); +``` + +### Phase 3: Document List Component (Week 2) + +#### Test Cases +1. **Test**: DocumentList renders loading state initially +2. **Test**: DocumentList displays all documents after loading +3. **Test**: Each document shows name, category, and file type +4. **Test**: Document list is empty state when no documents +5. **Test**: Error state displays when API fails +6. **Test**: Documents are grouped by category +7. **Test**: Clicking document opens preview/download + +#### Implementation +```typescript +// src/components/DocumentList.tsx +import { useEffect, useState } from 'react'; +import { ProductDocument } from '../types'; +import { googleDriveService } from '../services'; + +export function DocumentList() { + const [documents, setDocuments] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + fetchDocuments(); + }, []); + + // Implementation + + return ( +
+ {/* Render logic */} +
+ ); +} +``` + +#### Test File +```typescript +// src/components/__tests__/DocumentList.test.tsx +import { render, screen, waitFor } from '@testing-library/react'; +import { DocumentList } from '../DocumentList'; + +describe('DocumentList', () => { + it('renders loading state initially', () => { + render(); + expect(screen.getByText(/loading/i)).toBeInTheDocument(); + }); + + it('displays documents after successful fetch', async () => { + render(); + await waitFor(() => { + expect(screen.getByText(/Micra VR2 technical specifications/i)) + .toBeInTheDocument(); + }); + }); + + it('groups documents by category', async () => { + render(); + await waitFor(() => { + expect(screen.getByText(/Technical Specifications/i)) + .toBeInTheDocument(); + expect(screen.getByText(/Product Brochure/i)) + .toBeInTheDocument(); + }); + }); +}); +``` + +### Phase 4: Search & Filter Component (Week 2-3) + +#### Test Cases +1. **Test**: Search input filters documents by name +2. **Test**: Search is case-insensitive +3. **Test**: Category filter shows only selected category +4. **Test**: Multiple filters work together (AND logic) +5. **Test**: Product family filter works correctly +6. **Test**: File type filter (PDF, HTML, etc.) works +7. **Test**: "Clear filters" button resets all filters +8. **Test**: Search debouncing prevents excessive API calls +9. **Test**: No results message appears when filters return empty + +#### Implementation +```typescript +// src/components/SearchFilter.tsx +import { useState, useCallback } from 'react'; +import { Search, Filter } from 'lucide-react'; +import { debounce } from '../utils'; + +interface SearchFilterProps { + onFilterChange: (filters: FilterState) => void; +} + +export function SearchFilter({ onFilterChange }: SearchFilterProps) { + const [searchQuery, setSearchQuery] = useState(''); + const [selectedCategory, setSelectedCategory] = useState('all'); + + const debouncedSearch = useCallback( + debounce((query: string) => { + onFilterChange({ searchQuery: query, category: selectedCategory }); + }, 300), + [selectedCategory] + ); + + // Implementation +} +``` + +#### Test File +```typescript +// src/components/__tests__/SearchFilter.test.tsx +describe('SearchFilter', () => { + it('filters documents by search query', async () => { + const onFilterChange = vi.fn(); + render(); + + const input = screen.getByPlaceholderText(/search/i); + await userEvent.type(input, 'Micra VR2'); + + await waitFor(() => { + expect(onFilterChange).toHaveBeenCalledWith( + expect.objectContaining({ searchQuery: 'Micra VR2' }) + ); + }); + }); +}); +``` + +### Phase 5: Category Navigation (Week 3) + +#### Test Cases +1. **Test**: Sidebar shows all unique categories +2. **Test**: Category count badge shows correct number +3. **Test**: Clicking category filters document list +4. **Test**: Active category is visually highlighted +5. **Test**: "All Documents" option shows all +6. **Test**: Responsive sidebar collapses on mobile + +#### Implementation +```typescript +// src/components/CategorySidebar.tsx +import { DocumentCategory } from '../types'; + +interface CategorySidebarProps { + categories: Array<{ name: string; count: number }>; + activeCategory: string; + onCategorySelect: (category: string) => void; +} + +export function CategorySidebar({ + categories, + activeCategory, + onCategorySelect +}: CategorySidebarProps) { + return ( + + ); +} +``` + +### Phase 6: Document Preview Component (Week 3-4) + +#### Test Cases +1. **Test**: PDF documents show inline viewer +2. **Test**: Download button is available for all documents +3. **Test**: HTML documents render in iframe +4. **Test**: Document metadata displays (size, date, type) +5. **Test**: "Open in Google Drive" link works +6. **Test**: Modal closes on ESC key or backdrop click +7. **Test**: Loading state while document loads + +#### Implementation +```typescript +// src/components/DocumentPreview.tsx +import { FileText, Download, ExternalLink } from 'lucide-react'; +import { ProductDocument } from '../types'; + +interface DocumentPreviewProps { + document: ProductDocument; + onClose: () => void; +} + +export function DocumentPreview({ document, onClose }: DocumentPreviewProps) { + return ( +
+
e.stopPropagation()}> +
+

{document.name}

+ +
+ +
+ {document.fileType === 'pdf' && ( +