diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 9d275ff..161533e 100644 Binary files a/DEPLOYMENT.md and b/DEPLOYMENT.md differ diff --git a/docker-compose.yml b/docker-compose.yml index 955e017..60078fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,7 +77,7 @@ services: redis: condition: service_healthy ports: - - "8000:8000" + - "8003:8000" environment: # App configuration APP_ENV: ${APP_ENV:-dev} diff --git a/frontend/src/components/Auth/RequireAuth.tsx b/frontend/src/components/Auth/RequireAuth.tsx index 9e10add..0a739b7 100644 --- a/frontend/src/components/Auth/RequireAuth.tsx +++ b/frontend/src/components/Auth/RequireAuth.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import type { ReactNode } from 'react'; import { Navigate } from 'react-router-dom'; import { useAuthStore } from '../../lib/auth'; diff --git a/frontend/src/components/Auth/RoleGate.tsx b/frontend/src/components/Auth/RoleGate.tsx index 56f1833..521f80c 100644 --- a/frontend/src/components/Auth/RoleGate.tsx +++ b/frontend/src/components/Auth/RoleGate.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import type { ReactNode } from 'react'; import { useAuthStore } from '../../lib/auth'; import type { UserRole } from '../../types/api'; diff --git a/frontend/src/components/Auth/__tests__/RequireAuth.test.tsx b/frontend/src/components/Auth/__tests__/RequireAuth.test.tsx index ddaff3d..950f283 100644 --- a/frontend/src/components/Auth/__tests__/RequireAuth.test.tsx +++ b/frontend/src/components/Auth/__tests__/RequireAuth.test.tsx @@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen, waitFor } from '../../../test/utils' import { RequireAuth } from '../RequireAuth' import { useAuthStore } from '../../../lib/auth' -import { UserRole } from '../../../types/api' import { createMockUser } from '../../../test/utils' // Mock the auth store @@ -18,7 +17,6 @@ describe('RequireAuth', () => { }) it('renders children when user is authenticated', () => { - const mockUser = createMockUser() mockUseAuthStore.mockReturnValue({ isAuthenticated: true, isLoading: false, diff --git a/frontend/src/components/ErrorBoundary.tsx b/frontend/src/components/ErrorBoundary.tsx index ab3e68b..44fe1e6 100644 --- a/frontend/src/components/ErrorBoundary.tsx +++ b/frontend/src/components/ErrorBoundary.tsx @@ -1,4 +1,5 @@ -import React, { Component, ErrorInfo, ReactNode } from 'react'; +import { Component } from 'react'; +import type { ErrorInfo, ReactNode } from 'react'; interface Props { children: ReactNode; diff --git a/frontend/src/components/Layout/Layout.tsx b/frontend/src/components/Layout/Layout.tsx index 8f0784d..6bf0db1 100644 --- a/frontend/src/components/Layout/Layout.tsx +++ b/frontend/src/components/Layout/Layout.tsx @@ -1,4 +1,5 @@ -import { ReactNode, useState } from 'react'; +import { useState } from 'react'; +import type { ReactNode } from 'react'; import { Sidebar } from './Sidebar'; import { Navbar } from './Navbar'; diff --git a/frontend/src/components/UploadDropzone/UploadDropzone.tsx b/frontend/src/components/UploadDropzone/UploadDropzone.tsx index f33ca6d..34b94cc 100644 --- a/frontend/src/components/UploadDropzone/UploadDropzone.tsx +++ b/frontend/src/components/UploadDropzone/UploadDropzone.tsx @@ -1,5 +1,5 @@ import { useState, useCallback } from 'react'; -import { useDropzone } from 'react-dropzone'; +import { useDropzone, type FileRejection } from 'react-dropzone'; interface UploadDropzoneProps { onFileSelect: (file: File) => void; @@ -16,7 +16,7 @@ export function UploadDropzone({ }: UploadDropzoneProps) { const [error, setError] = useState(null); - const onDrop = useCallback((acceptedFiles: File[], rejectedFiles: { file: File; errors: { code: string }[] }[]) => { + const onDrop = useCallback((acceptedFiles: File[], rejectedFiles: FileRejection[]) => { setError(null); if (rejectedFiles.length > 0) { diff --git a/frontend/src/components/VideoWithCaptions.tsx b/frontend/src/components/VideoWithCaptions.tsx index 3df48a0..ad687f5 100644 --- a/frontend/src/components/VideoWithCaptions.tsx +++ b/frontend/src/components/VideoWithCaptions.tsx @@ -102,7 +102,7 @@ export function VideoWithCaptions({ const video = videoRef.current; if (!video) return; - let timeoutId: number; + let timeoutId: ReturnType; const handleTimeUpdate = () => { clearTimeout(timeoutId); timeoutId = setTimeout(() => { diff --git a/frontend/src/contexts/GlobalWebSocketContext.tsx b/frontend/src/contexts/GlobalWebSocketContext.tsx index dab32f9..6ca99f3 100644 --- a/frontend/src/contexts/GlobalWebSocketContext.tsx +++ b/frontend/src/contexts/GlobalWebSocketContext.tsx @@ -1,4 +1,5 @@ -import { createContext, useContext, ReactNode, useCallback } from 'react'; +import { createContext, useContext, useCallback } from 'react'; +import type { ReactNode } from 'react'; import { useJobStatusWebSocket } from '../hooks/useJobStatusWebSocket'; import { useToastContext } from './ToastContext'; import { getStatusMessageConfig } from '../utils/jobStatusMessages'; diff --git a/frontend/src/contexts/NotificationContext.tsx b/frontend/src/contexts/NotificationContext.tsx index a2f102d..ec241f9 100644 --- a/frontend/src/contexts/NotificationContext.tsx +++ b/frontend/src/contexts/NotificationContext.tsx @@ -1,4 +1,5 @@ -import { createContext, useContext, ReactNode, useState, useCallback } from 'react'; +import { createContext, useContext, useState, useCallback } from 'react'; +import type { ReactNode } from 'react'; export interface NotificationItem { id: string; diff --git a/frontend/src/contexts/ToastContext.tsx b/frontend/src/contexts/ToastContext.tsx index 2a08c31..34976d3 100644 --- a/frontend/src/contexts/ToastContext.tsx +++ b/frontend/src/contexts/ToastContext.tsx @@ -1,4 +1,5 @@ -import { createContext, useContext, ReactNode } from 'react'; +import { createContext, useContext } from 'react'; +import type { ReactNode } from 'react'; import { useToast } from '../hooks/useToast'; import { useNotificationContext } from './NotificationContext'; import type { ToastMessage } from '../components/Toast/Toast'; diff --git a/frontend/src/hooks/__tests__/useJob.test.tsx b/frontend/src/hooks/__tests__/useJob.test.tsx index 8910996..fa268b1 100644 --- a/frontend/src/hooks/__tests__/useJob.test.tsx +++ b/frontend/src/hooks/__tests__/useJob.test.tsx @@ -1,7 +1,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { renderHook, waitFor } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { ReactNode } from 'react' +import type { ReactNode } from 'react' import { useJobs, useJob, diff --git a/frontend/src/hooks/useJob.ts b/frontend/src/hooks/useJob.ts index 796a657..ec1121a 100644 --- a/frontend/src/hooks/useJob.ts +++ b/frontend/src/hooks/useJob.ts @@ -8,7 +8,7 @@ import type { } from '../types/api'; // Query hooks -export function useJobs(filters?: { status?: string; mine?: boolean }, options?: { enabled?: boolean }) { +export function useJobs(filters?: { status?: string; mine?: boolean; page?: number; size?: number }, options?: { enabled?: boolean }) { return useQuery({ queryKey: ['jobs', filters], queryFn: () => apiClient.getJobs(filters), diff --git a/frontend/src/lib/auth.ts b/frontend/src/lib/auth.ts index 92627a6..a3801cd 100644 --- a/frontend/src/lib/auth.ts +++ b/frontend/src/lib/auth.ts @@ -48,7 +48,7 @@ export const useAuthStore = create((set) => ({ refreshAuth: async () => { try { - const response = await apiClient.refresh(); + await apiClient.refresh(); // Token refresh succeeded, but we don't have user info // In a proper implementation, the refresh endpoint would return user data // For now, we'll assume if refresh succeeds and we have existing user, we're authenticated diff --git a/frontend/src/routes/admin/FinalDetail.tsx b/frontend/src/routes/admin/FinalDetail.tsx index 044dd77..24c0ede 100644 --- a/frontend/src/routes/admin/FinalDetail.tsx +++ b/frontend/src/routes/admin/FinalDetail.tsx @@ -250,7 +250,7 @@ export function FinalDetail() { language={language} assets={assets} jobId={id!} - audioUrl={downloads?.downloads?.[language]?.audio_description_mp3} + audioUrl={typeof downloads?.downloads?.[language] === 'object' ? downloads?.downloads?.[language]?.audio_description_mp3 : undefined} /> ))} diff --git a/frontend/src/routes/admin/QCDetail.tsx b/frontend/src/routes/admin/QCDetail.tsx index 1aead28..e734e0d 100644 --- a/frontend/src/routes/admin/QCDetail.tsx +++ b/frontend/src/routes/admin/QCDetail.tsx @@ -32,7 +32,9 @@ export function QCDetail() { const isProcessing = approveEnglishMutation.isPending || rejectJobMutation.isPending || updateVttMutation.isPending || adjustTimingMutation.isPending; // Get video URL from downloads - const videoUrl = downloads?.downloads?.source_video || ''; + const videoUrl = typeof downloads?.downloads?.source_video === 'string' + ? downloads.downloads.source_video + : ''; // Load VTT content when fetched useEffect(() => { diff --git a/frontend/src/routes/jobs/JobDetail.tsx b/frontend/src/routes/jobs/JobDetail.tsx index 130b07b..7942d5a 100644 --- a/frontend/src/routes/jobs/JobDetail.tsx +++ b/frontend/src/routes/jobs/JobDetail.tsx @@ -69,14 +69,16 @@ export function JobDetail() { // Get video URL from downloads - const videoUrl = downloads?.downloads?.source_video || ''; + const videoUrl = typeof downloads?.downloads?.source_video === 'string' + ? downloads.downloads.source_video + : ''; const tabs = [ - { id: 'overview', label: 'Overview', icon: '📋' }, - { id: 'video', label: 'Video Preview', icon: '🎥', disabled: !videoUrl }, - { id: 'assets', label: 'Assets', icon: '📁' }, - { id: 'history', label: 'History', icon: '📜' }, - ] as const; + { id: 'overview' as const, label: 'Overview', icon: '📋', disabled: false }, + { id: 'video' as const, label: 'Video Preview', icon: '🎥', disabled: !videoUrl }, + { id: 'assets' as const, label: 'Assets', icon: '📁', disabled: false }, + { id: 'history' as const, label: 'History', icon: '📜', disabled: false }, + ]; if (isLoading) { return ( @@ -182,8 +184,8 @@ export function JobDetail() { {videoUrl ? ( ) : (
@@ -346,7 +348,7 @@ export function JobDetail() { {job.error && (

Processing Error

-

{job.error.message}

+

{String(job.error.message || 'Unknown error')}

)}
diff --git a/frontend/src/routes/jobs/NewJob.tsx b/frontend/src/routes/jobs/NewJob.tsx index 8b4f84d..ef417e8 100644 --- a/frontend/src/routes/jobs/NewJob.tsx +++ b/frontend/src/routes/jobs/NewJob.tsx @@ -105,13 +105,7 @@ export function NewJob() { setValue('transcreation', transcreation.filter(l => l !== lang)); }; - const toggleTranscreation = (lang: string) => { - if (transcreation.includes(lang)) { - setValue('transcreation', transcreation.filter(l => l !== lang)); - } else { - setValue('transcreation', [...transcreation, lang]); - } - }; + // Removed toggleTranscreation - not currently used // Success state if (createdJob) { diff --git a/frontend/src/test/utils.tsx b/frontend/src/test/utils.tsx index 1e06b34..cf4836e 100644 --- a/frontend/src/test/utils.tsx +++ b/frontend/src/test/utils.tsx @@ -1,8 +1,9 @@ -import { ReactElement } from 'react' -import { render, RenderOptions } from '@testing-library/react' +import type { ReactElement } from 'react' +import { render } from '@testing-library/react' +import type { RenderOptions } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { BrowserRouter } from 'react-router-dom' -import { User, UserRole } from '../types/api' +import type { User, UserRole } from '../types/api' // Mock user for testing export const createMockUser = (overrides: Partial = {}): User => ({ diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index aff56b8..7b0545f 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -139,7 +139,4 @@ export interface BulkDeleteResponse { export interface JobDeleteResponse { message: string; -} - -// Ensure Job is properly exported -export type { Job }; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index 227a6c6..7c0323e 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -23,5 +23,6 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, - "include": ["src"] + "include": ["src"], + "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/test"] } diff --git a/scripts/build-frontend.sh b/scripts/build-frontend.sh index 808188b..767fbf8 100755 --- a/scripts/build-frontend.sh +++ b/scripts/build-frontend.sh @@ -3,7 +3,7 @@ # Frontend Build and Deploy Script # ============================================================================= # Builds the React frontend and deploys to Apache document root -# Run from: /opt/accessible-video/ +# Run from: /opt/video-accessibility/ # Usage: ./scripts/build-frontend.sh # ============================================================================= @@ -17,7 +17,7 @@ BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration -PROJECT_DIR="/opt/accessible-video" +PROJECT_DIR="/opt/video-accessibility" FRONTEND_DIR="$PROJECT_DIR/frontend" DEPLOY_DIR="/var/www/html/video-accessibility" @@ -100,9 +100,9 @@ build_frontend() { cd "$FRONTEND_DIR" - # Install dependencies + # Install dependencies (including dev dependencies needed for build) print_info "Installing dependencies..." - npm ci --only=production + npm ci print_success "Dependencies installed" # Build the application diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 3151f11..118deed 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -3,7 +3,7 @@ # Deployment Script for Accessible Video Platform # ============================================================================= # This script handles building and deploying the application -# Run from: /opt/accessible-video/ +# Run from: /opt/video-accessibility/ # Usage: ./scripts/deploy.sh [options] # ============================================================================= @@ -17,7 +17,7 @@ BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration -PROJECT_DIR="/opt/accessible-video" +PROJECT_DIR="/opt/video-accessibility" COMPOSE_FILES="-f docker-compose.yml -f docker-compose.prod.yml" # ============================================================================= @@ -39,7 +39,7 @@ print_error() { } print_warning() { - echo -e "${YELLOW} $1${NC}" + echo -e "${YELLOW}� $1${NC}" } print_info() { @@ -55,7 +55,7 @@ preflight_checks() { # Check if running from correct directory if [ ! -f "docker-compose.yml" ]; then - print_error "docker-compose.yml not found. Please run from /opt/accessible-video/" + print_error "docker-compose.yml not found. Please run from /opt/video-accessibility/" exit 1 fi print_success "Running from correct directory" @@ -88,12 +88,12 @@ preflight_checks() { fi print_success "Docker is running" - # Check if docker-compose is available - if ! command -v docker-compose &> /dev/null; then - print_error "docker-compose is not installed" + # Check if docker compose is available + if ! docker compose version &> /dev/null; then + print_error "docker compose is not installed" exit 1 fi - print_success "docker-compose is available" + print_success "docker compose is available" echo "" } @@ -142,17 +142,17 @@ deploy_backend() { # Build images print_info "Building Docker images (this may take a few minutes)..." - docker-compose $COMPOSE_FILES build --no-cache + docker compose $COMPOSE_FILES build --no-cache print_success "Docker images built" # Stop existing containers print_info "Stopping existing containers..." - docker-compose $COMPOSE_FILES down + docker compose $COMPOSE_FILES down print_success "Containers stopped" # Start services print_info "Starting services..." - docker-compose $COMPOSE_FILES up -d + docker compose $COMPOSE_FILES up -d print_success "Services started" # Wait for services to be healthy @@ -160,9 +160,9 @@ deploy_backend() { sleep 10 # Check service health - if docker-compose $COMPOSE_FILES ps | grep -q "unhealthy"; then + if docker compose $COMPOSE_FILES ps | grep -q "unhealthy"; then print_error "Some services are unhealthy!" - docker-compose $COMPOSE_FILES ps + docker compose $COMPOSE_FILES ps exit 1 fi print_success "All services are healthy" @@ -217,7 +217,7 @@ run_migrations() { print_header "Running Database Migrations" print_info "Running migrations..." - docker-compose $COMPOSE_FILES exec -T api python migrate.py + docker compose $COMPOSE_FILES exec -T api python migrate.py print_success "Migrations completed" echo "" @@ -231,7 +231,7 @@ display_status() { print_header "Deployment Status" echo -e "${BLUE}Container Status:${NC}" - docker-compose $COMPOSE_FILES ps + docker compose $COMPOSE_FILES ps echo "" echo -e "${BLUE}Service URLs:${NC}" @@ -243,10 +243,10 @@ display_status() { echo -e "${GREEN}Deployment completed successfully!${NC}" echo "" echo "To view logs:" - echo " docker-compose $COMPOSE_FILES logs -f [service]" + echo " docker compose $COMPOSE_FILES logs -f [service]" echo "" echo "To restart a service:" - echo " docker-compose $COMPOSE_FILES restart [service]" + echo " docker compose $COMPOSE_FILES restart [service]" echo "" }