diff --git a/frontend/index.html b/frontend/index.html
index 8408550..8e4f623 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -3,24 +3,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/frontend/package.json b/frontend/package.json
index db8399c..1ebe754 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -55,8 +55,6 @@
"tailwindcss": "^4.0.0",
"typescript": "^5.8.3",
"vite": "^7.0.0",
- "vite-plugin-pwa": "^1.1.0",
- "vitest": "^0.28.0",
- "workbox-window": "^7.3.0"
+ "vitest": "^0.28.0"
}
}
diff --git a/frontend/public/icons/apple-touch-icon.png b/frontend/public/icons/apple-touch-icon.png
deleted file mode 100644
index 2f42a4e..0000000
Binary files a/frontend/public/icons/apple-touch-icon.png and /dev/null differ
diff --git a/frontend/public/icons/icon-192-maskable.png b/frontend/public/icons/icon-192-maskable.png
deleted file mode 100644
index 466d378..0000000
Binary files a/frontend/public/icons/icon-192-maskable.png and /dev/null differ
diff --git a/frontend/public/icons/icon-192.png b/frontend/public/icons/icon-192.png
deleted file mode 100644
index c4007f4..0000000
Binary files a/frontend/public/icons/icon-192.png and /dev/null differ
diff --git a/frontend/public/icons/icon-512-maskable.png b/frontend/public/icons/icon-512-maskable.png
deleted file mode 100644
index b676002..0000000
Binary files a/frontend/public/icons/icon-512-maskable.png and /dev/null differ
diff --git a/frontend/public/icons/icon-512.png b/frontend/public/icons/icon-512.png
deleted file mode 100644
index 2a41f4c..0000000
Binary files a/frontend/public/icons/icon-512.png and /dev/null differ
diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json
deleted file mode 100644
index 41baa66..0000000
--- a/frontend/public/manifest.json
+++ /dev/null
@@ -1,77 +0,0 @@
-{
- "name": "Readur - Document Intelligence Platform",
- "short_name": "Readur",
- "description": "AI-powered document management with OCR and intelligent search",
- "start_url": "/",
- "scope": "/",
- "display": "standalone",
- "background_color": "#ffffff",
- "theme_color": "#6366f1",
- "orientation": "portrait-primary",
- "icons": [
- {
- "src": "/readur-32.png",
- "sizes": "32x32",
- "type": "image/png"
- },
- {
- "src": "/readur-64.png",
- "sizes": "64x64",
- "type": "image/png"
- },
- {
- "src": "/icons/icon-192.png",
- "sizes": "192x192",
- "type": "image/png",
- "purpose": "any"
- },
- {
- "src": "/icons/icon-512.png",
- "sizes": "512x512",
- "type": "image/png",
- "purpose": "any"
- },
- {
- "src": "/icons/icon-192-maskable.png",
- "sizes": "192x192",
- "type": "image/png",
- "purpose": "maskable"
- },
- {
- "src": "/icons/icon-512-maskable.png",
- "sizes": "512x512",
- "type": "image/png",
- "purpose": "maskable"
- }
- ],
- "categories": ["productivity", "utilities", "business"],
- "shortcuts": [
- {
- "name": "Upload Document",
- "short_name": "Upload",
- "description": "Upload a new document",
- "url": "/upload",
- "icons": [
- {
- "src": "/icons/icon-192.png",
- "sizes": "192x192"
- }
- ]
- },
- {
- "name": "Search Documents",
- "short_name": "Search",
- "description": "Search your documents",
- "url": "/search",
- "icons": [
- {
- "src": "/icons/icon-192.png",
- "sizes": "192x192"
- }
- ]
- }
- ],
- "screenshots": [],
- "display_override": ["standalone", "minimal-ui"],
- "prefer_related_applications": false
-}
diff --git a/frontend/public/offline.html b/frontend/public/offline.html
deleted file mode 100644
index 36cbb76..0000000
--- a/frontend/public/offline.html
+++ /dev/null
@@ -1,168 +0,0 @@
-
-
-
-
-
- Offline - Readur
-
-
-
-
-
-
-
You're Offline
-
It looks like you've lost your internet connection. Don't worry, Readur will be back once you're online again.
-
-
-
Checking connection...
-
-
-
-
-
-
-
-
diff --git a/frontend/src/components/DocumentViewer.tsx b/frontend/src/components/DocumentViewer.tsx
index 2645be8..9081c72 100644
--- a/frontend/src/components/DocumentViewer.tsx
+++ b/frontend/src/components/DocumentViewer.tsx
@@ -1,19 +1,11 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect } from 'react';
import {
Box,
Typography,
CircularProgress,
Alert,
Paper,
- IconButton,
- useTheme,
- useMediaQuery,
} from '@mui/material';
-import {
- ZoomIn as ZoomInIcon,
- ZoomOut as ZoomOutIcon,
- RestartAlt as ResetIcon,
-} from '@mui/icons-material';
import { documentService } from '../services/api';
interface DocumentViewerProps {
@@ -63,7 +55,29 @@ const DocumentViewer: React.FC = ({
// Handle images
if (mimeType.startsWith('image/')) {
- return ;
+ return (
+
+
+
+ );
}
// Handle PDFs
@@ -138,200 +152,6 @@ const DocumentViewer: React.FC = ({
);
};
-// Component for viewing images with touch gestures
-const ImageViewer: React.FC<{ documentUrl: string; filename: string }> = ({
- documentUrl,
- filename,
-}) => {
- const theme = useTheme();
- const isMobile = useMediaQuery(theme.breakpoints.down('md'));
- const [scale, setScale] = useState(1);
- const [position, setPosition] = useState({ x: 0, y: 0 });
- const imageContainerRef = useRef(null);
- const imageRef = useRef(null);
- const lastTouchDistanceRef = useRef(null);
- const lastTapTimeRef = useRef(0);
-
- // Reset zoom and position
- const handleReset = () => {
- setScale(1);
- setPosition({ x: 0, y: 0 });
- };
-
- // Zoom in
- const handleZoomIn = () => {
- setScale((prev) => Math.min(prev + 0.5, 5));
- };
-
- // Zoom out
- const handleZoomOut = () => {
- setScale((prev) => Math.max(prev - 0.5, 0.5));
- };
-
- // Handle double tap to zoom
- const handleDoubleClick = (e: React.MouseEvent) => {
- const now = Date.now();
- const timeSinceLastTap = now - lastTapTimeRef.current;
-
- if (timeSinceLastTap < 300) {
- // Double tap detected
- if (scale === 1) {
- setScale(2);
- } else {
- handleReset();
- }
- }
- lastTapTimeRef.current = now;
- };
-
- // Handle pinch-to-zoom
- const handleTouchStart = (e: React.TouchEvent) => {
- if (e.touches.length === 2) {
- const distance = Math.hypot(
- e.touches[0].clientX - e.touches[1].clientX,
- e.touches[0].clientY - e.touches[1].clientY
- );
- lastTouchDistanceRef.current = distance;
- }
- };
-
- const handleTouchMove = (e: React.TouchEvent) => {
- if (e.touches.length === 2 && lastTouchDistanceRef.current) {
- e.preventDefault();
- const distance = Math.hypot(
- e.touches[0].clientX - e.touches[1].clientX,
- e.touches[0].clientY - e.touches[1].clientY
- );
- const scaleDelta = distance / lastTouchDistanceRef.current;
- setScale((prev) => Math.max(0.5, Math.min(5, prev * scaleDelta)));
- lastTouchDistanceRef.current = distance;
- }
- };
-
- const handleTouchEnd = () => {
- lastTouchDistanceRef.current = null;
- };
-
- // Handle wheel zoom
- const handleWheel = (e: React.WheelEvent) => {
- e.preventDefault();
- const delta = e.deltaY > 0 ? -0.1 : 0.1;
- setScale((prev) => Math.max(0.5, Math.min(5, prev + delta)));
- };
-
- return (
-
- {/* Zoom Controls */}
- {isMobile && (
-
-
-
-
-
-
-
- = 5}
- sx={{
- color: 'white',
- '&:disabled': { color: 'rgba(255,255,255,0.3)' },
- }}
- >
-
-
-
- )}
-
- {/* Image Container */}
- 1 ? 'move' : 'zoom-in',
- touchAction: 'none',
- userSelect: 'none',
- WebkitUserSelect: 'none',
- }}
- >
-
-
-
- {/* Zoom Indicator */}
- {isMobile && scale !== 1 && (
-
- {Math.round(scale * 100)}%
-
- )}
-
- );
-};
-
// Component for viewing text files
const TextFileViewer: React.FC<{ documentUrl: string; filename: string }> = ({
documentUrl,
diff --git a/frontend/src/components/GlobalSearchBar/GlobalSearchBar.tsx b/frontend/src/components/GlobalSearchBar/GlobalSearchBar.tsx
index 5bf20fe..509d2a8 100644
--- a/frontend/src/components/GlobalSearchBar/GlobalSearchBar.tsx
+++ b/frontend/src/components/GlobalSearchBar/GlobalSearchBar.tsx
@@ -367,8 +367,8 @@ const GlobalSearchBar: React.FC = ({ sx, ...props }) => {
}}
sx={{
width: '100%',
- minWidth: { xs: 0, sm: 400, md: 600 },
- maxWidth: { xs: '100%', sm: 600, md: 800, lg: 1200 },
+ minWidth: 600,
+ maxWidth: 1200,
'& .MuiOutlinedInput-root': {
background: theme.palette.mode === 'light'
? 'linear-gradient(135deg, rgba(255,255,255,0.95) 0%, rgba(248,250,252,0.90) 100%)'
diff --git a/frontend/src/components/Layout/AppLayout.tsx b/frontend/src/components/Layout/AppLayout.tsx
index 0d70762..03a2a55 100644
--- a/frontend/src/components/Layout/AppLayout.tsx
+++ b/frontend/src/components/Layout/AppLayout.tsx
@@ -46,7 +46,6 @@ import GlobalSearchBar from '../GlobalSearchBar';
import ThemeToggle from '../ThemeToggle/ThemeToggle';
import NotificationPanel from '../Notifications/NotificationPanel';
import LanguageSwitcher from '../LanguageSwitcher';
-import BottomNavigation from './BottomNavigation';
import { useTranslation } from 'react-i18next';
const drawerWidth = 280;
@@ -422,15 +421,9 @@ const AppLayout: React.FC = ({ children }) => {
boxShadow: theme.palette.mode === 'light'
? '0 4px 32px rgba(0,0,0,0.04)'
: '0 4px 32px rgba(0,0,0,0.2)',
- // iOS safe area support
- paddingTop: 'env(safe-area-inset-top, 0px)',
}}
>
-
+
= ({ children }) => {
: t('navigation.dashboard')}
- {/* Global Search Bar - Hidden on mobile, use search page instead */}
-
+ {/* Global Search Bar */}
+
@@ -671,37 +657,18 @@ const AppLayout: React.FC = ({ children }) => {
width: { md: `calc(100% - ${drawerWidth}px)` },
minHeight: '100vh',
backgroundColor: 'background.default',
- // Add padding for bottom navigation on mobile
- paddingBottom: {
- xs: 'calc(64px + env(safe-area-inset-bottom, 0px))',
- md: 0,
- },
}}
>
-
+
{children}
- {/* Bottom Navigation (Mobile Only) */}
-
-
{/* Notification Panel */}
-
);
diff --git a/frontend/src/components/Layout/BottomNavigation.tsx b/frontend/src/components/Layout/BottomNavigation.tsx
deleted file mode 100644
index 780fb35..0000000
--- a/frontend/src/components/Layout/BottomNavigation.tsx
+++ /dev/null
@@ -1,185 +0,0 @@
-import React from 'react';
-import {
- BottomNavigation as MuiBottomNavigation,
- BottomNavigationAction,
- Paper,
- useTheme,
-} from '@mui/material';
-import {
- Dashboard as DashboardIcon,
- CloudUpload as UploadIcon,
- Search as SearchIcon,
- Settings as SettingsIcon,
-} from '@mui/icons-material';
-import { useNavigate, useLocation } from 'react-router-dom';
-import { useTranslation } from 'react-i18next';
-
-const BottomNavigation: React.FC = () => {
- const navigate = useNavigate();
- const location = useLocation();
- const theme = useTheme();
- const { t } = useTranslation();
-
- // Map paths to nav values
- const getNavValue = (pathname: string): string => {
- if (pathname === '/dashboard') return 'dashboard';
- if (pathname === '/upload') return 'upload';
- if (pathname === '/search' || pathname === '/documents') return 'search';
- if (pathname === '/settings' || pathname === '/profile') return 'settings';
- return 'dashboard';
- };
-
- const handleNavigation = (_event: React.SyntheticEvent, newValue: string) => {
- switch (newValue) {
- case 'dashboard':
- navigate('/dashboard');
- break;
- case 'upload':
- navigate('/upload');
- break;
- case 'search':
- navigate('/documents');
- break;
- case 'settings':
- navigate('/settings');
- break;
- }
- };
-
- return (
-
-
- }
- sx={{
- '&.Mui-selected': {
- '& .MuiBottomNavigationAction-label': {
- background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
- backgroundClip: 'text',
- WebkitBackgroundClip: 'text',
- WebkitTextFillColor: 'transparent',
- fontWeight: 600,
- },
- },
- }}
- />
- }
- sx={{
- '&.Mui-selected': {
- '& .MuiBottomNavigationAction-label': {
- background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
- backgroundClip: 'text',
- WebkitBackgroundClip: 'text',
- WebkitTextFillColor: 'transparent',
- fontWeight: 600,
- },
- },
- }}
- />
- }
- sx={{
- '&.Mui-selected': {
- '& .MuiBottomNavigationAction-label': {
- background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
- backgroundClip: 'text',
- WebkitBackgroundClip: 'text',
- WebkitTextFillColor: 'transparent',
- fontWeight: 600,
- },
- },
- }}
- />
- }
- sx={{
- '&.Mui-selected': {
- '& .MuiBottomNavigationAction-label': {
- background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
- backgroundClip: 'text',
- WebkitBackgroundClip: 'text',
- WebkitTextFillColor: 'transparent',
- fontWeight: 600,
- },
- },
- }}
- />
-
-
- );
-};
-
-export default BottomNavigation;
diff --git a/frontend/src/index.css b/frontend/src/index.css
index c42fd0e..5e001a1 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -2,97 +2,6 @@
@tailwind components;
@tailwind utilities;
-/* ============================================
- PWA & iOS Safe Area Support
- ============================================ */
-
-/* Ensure the entire app respects iOS safe areas */
-:root {
- /* Define safe area insets for iOS devices */
- --safe-area-inset-top: env(safe-area-inset-top, 0px);
- --safe-area-inset-right: env(safe-area-inset-right, 0px);
- --safe-area-inset-bottom: env(safe-area-inset-bottom, 0px);
- --safe-area-inset-left: env(safe-area-inset-left, 0px);
-}
-
-/* PWA-specific styles */
-@media all and (display-mode: standalone) {
- /* Remove system tap highlight on iOS PWA */
- * {
- -webkit-tap-highlight-color: transparent;
- }
-
- /* Prevent pull-to-refresh on iOS */
- body {
- overscroll-behavior-y: contain;
- }
-
- /* Improve iOS PWA scrolling performance */
- html {
- -webkit-overflow-scrolling: touch;
- }
-}
-
-/* iOS-specific optimizations */
-@supports (-webkit-touch-callout: none) {
- /* Disable callout on iOS when long-pressing */
- * {
- -webkit-touch-callout: none;
- }
-
- /* Allow callout on text content */
- p, span, div[contenteditable="true"] {
- -webkit-touch-callout: default;
- }
-
- /* Smooth momentum scrolling on iOS */
- .scrollable-content {
- -webkit-overflow-scrolling: touch;
- }
-
- /* Fix iOS input zoom issue */
- input, textarea, select {
- font-size: 16px !important;
- }
-}
-
-/* Touch-optimized tap targets (minimum 44x44px for iOS) */
-@media (pointer: coarse) {
- button,
- a,
- .MuiIconButton-root,
- .MuiButton-root,
- .MuiChip-root.MuiChip-clickable {
- min-height: 44px;
- min-width: 44px;
- }
-
- /* Bottom navigation touch targets */
- .MuiBottomNavigationAction-root {
- min-height: 56px;
- }
-}
-
-/* Prevent iOS double-tap zoom on buttons */
-button,
-input[type="button"],
-input[type="submit"] {
- touch-action: manipulation;
-}
-
-/* iOS status bar color adaptation */
-@media (prefers-color-scheme: dark) {
- html {
- background-color: #1e1e1e;
- }
-}
-
-@media (prefers-color-scheme: light) {
- html {
- background-color: #ffffff;
- }
-}
-
/* Enhanced search responsiveness styles */
.search-input-responsive {
transition: all 0.2s ease-in-out;
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 861e63d..7b89620 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,6 +1,5 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
-import { VitePWA } from 'vite-plugin-pwa'
// Support environment variables for development
const BACKEND_PORT = process.env.BACKEND_PORT || '8000'
@@ -9,113 +8,7 @@ const CLIENT_PORT = process.env.CLIENT_PORT || '5173'
const PROXY_TARGET = process.env.VITE_API_PROXY_TARGET || `http://localhost:${BACKEND_PORT}`
export default defineConfig({
- plugins: [
- react(),
- VitePWA({
- registerType: 'autoUpdate',
- includeAssets: ['favicon.ico', 'readur-32.png', 'readur-64.png', 'icons/*.png', 'offline.html'],
- manifest: {
- name: 'Readur - Document Intelligence Platform',
- short_name: 'Readur',
- description: 'AI-powered document management with OCR and intelligent search',
- theme_color: '#6366f1',
- background_color: '#ffffff',
- display: 'standalone',
- orientation: 'portrait-primary',
- scope: '/',
- start_url: '/',
- icons: [
- {
- src: '/readur-32.png',
- sizes: '32x32',
- type: 'image/png'
- },
- {
- src: '/readur-64.png',
- sizes: '64x64',
- type: 'image/png'
- },
- {
- src: '/icons/icon-192.png',
- sizes: '192x192',
- type: 'image/png',
- purpose: 'any'
- },
- {
- src: '/icons/icon-512.png',
- sizes: '512x512',
- type: 'image/png',
- purpose: 'any'
- },
- {
- src: '/icons/icon-192-maskable.png',
- sizes: '192x192',
- type: 'image/png',
- purpose: 'maskable'
- },
- {
- src: '/icons/icon-512-maskable.png',
- sizes: '512x512',
- type: 'image/png',
- purpose: 'maskable'
- }
- ]
- },
- workbox: {
- globPatterns: ['**/*.{js,css,html,ico,png,svg,woff,woff2}'],
- globIgnores: ['**/readur.png'],
- maximumFileSizeToCacheInBytes: 3 * 1024 * 1024, // 3 MB limit
- // Exclude auth routes from navigation fallback and caching
- navigateFallbackDenylist: [/^\/api\/auth\//],
- runtimeCaching: [
- {
- urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
- handler: 'CacheFirst',
- options: {
- cacheName: 'google-fonts-cache',
- expiration: {
- maxEntries: 10,
- maxAgeSeconds: 60 * 60 * 24 * 365 // 1 year
- },
- cacheableResponse: {
- statuses: [0, 200]
- }
- }
- },
- {
- urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
- handler: 'CacheFirst',
- options: {
- cacheName: 'gstatic-fonts-cache',
- expiration: {
- maxEntries: 10,
- maxAgeSeconds: 60 * 60 * 24 * 365 // 1 year
- },
- cacheableResponse: {
- statuses: [0, 200]
- }
- }
- },
- {
- // Only cache non-auth API routes
- urlPattern: /^\/api\/(?!auth\/).*/,
- handler: 'NetworkFirst',
- options: {
- cacheName: 'api-cache',
- expiration: {
- maxEntries: 100,
- maxAgeSeconds: 60 * 5 // 5 minutes
- },
- networkTimeoutSeconds: 10
- }
- }
- ]
- },
- devOptions: {
- enabled: false // Disable PWA in dev mode for faster development
- }
- })
- ],
+ plugins: [react()],
test: {
environment: 'jsdom',
setupFiles: ['src/test/setup.ts'],