Fix frontend test setup and refresh docs

This commit is contained in:
Your Name
2026-03-25 04:32:04 +02:00
parent 6489ce2735
commit 14743c6cfe
8 changed files with 388 additions and 292 deletions

View File

@@ -1,6 +1,7 @@
# SaaS-PDF Project Status Report # SaaS-PDF Project Status Report
Generated on: 2026-03-10 Generated on: 2026-03-10
Updated on: 2026-03-25
Branch reviewed: feature/seo-content Branch reviewed: feature/seo-content
## Executive Summary ## Executive Summary
@@ -27,7 +28,7 @@ The main remaining gaps are consistency and production hardening:
### Backend ### Backend
- Flask application factory with 24 registered blueprints - Flask application factory with 33 registered blueprints
- Celery async task processing - Celery async task processing
- Redis-backed task flow - Redis-backed task flow
- Service-oriented architecture under backend/app/services - Service-oriented architecture under backend/app/services
@@ -133,7 +134,7 @@ Implemented pages:
Notes: Notes:
- Contact currently uses a mailto flow rather than a backend contact form endpoint. - Contact now uses a backend submission endpoint plus direct email fallback.
- About, Privacy, and Terms are SEO-enabled pages with structured metadata. - About, Privacy, and Terms are SEO-enabled pages with structured metadata.
## Phase 6 — Technical SEO Optimization ## Phase 6 — Technical SEO Optimization
@@ -215,11 +216,11 @@ The following improvements were started as part of this implementation step:
1. Refresh docs/tool_inventory.md so it becomes the current source of truth again. 1. Refresh docs/tool_inventory.md so it becomes the current source of truth again.
2. Remove duplicate Helmet metadata from tool components that are already wrapped by ToolLandingPage. 2. Remove duplicate Helmet metadata from tool components that are already wrapped by ToolLandingPage.
3. Replace placeholder domain values in public SEO files with the production domain. 3. Replace placeholder domain values in public SEO files with the production domain.
4. Decide whether contact should remain mailto-based or move to a backend endpoint. 4. Surface or intentionally defer the tools that exist in routes but are not shown on the homepage.
5. Run full backend and frontend test/build validation in the target environment. 5. Run full backend and frontend test/build validation in the target environment.
## Final Assessment ## Final Assessment
SaaS-PDF is no longer just a basic MVP. It is already a broad multi-tool document-processing platform with strong progress across product scope, frontend SEO architecture, and backend task-based processing. SaaS-PDF is no longer just a basic MVP. It is already a broad multi-tool document-processing platform with strong progress across product scope, frontend SEO architecture, and backend task-based processing.
The current priority is not missing core features. The current priority is tightening consistency, production configuration, and documentation so the implemented work is easier to maintain and safer to ship. The current priority is not missing core features. The current priority is tightening consistency, production configuration, homepage/catalog alignment, and documentation so the implemented work is easier to maintain and safer to ship.

View File

@@ -12,7 +12,7 @@
| **OpenGraph tags** | `og:title`, `og:description`, `og:url`, `og:type`, `og:site_name`, `og:locale` on all pages | | **OpenGraph tags** | `og:title`, `og:description`, `og:url`, `og:type`, `og:site_name`, `og:locale` on all pages |
| **Twitter cards** | `twitter:card`, `twitter:title`, `twitter:description` on all pages | | **Twitter cards** | `twitter:card`, `twitter:title`, `twitter:description` on all pages |
| **Structured data** | `WebSite`, `Organization`, `WebPage`, `WebApplication`, `BreadcrumbList`, `FAQPage` JSON-LD | | **Structured data** | `WebSite`, `Organization`, `WebPage`, `WebApplication`, `BreadcrumbList`, `FAQPage` JSON-LD |
| **Sitemap** | Auto-generated via `scripts/generate_sitemap.py` — 37 URLs (5 pages + 32 tools) | | **Sitemap** | Auto-generated via frontend SEO scripts — current committed snapshot contains 245 URLs across static pages, blog posts, tool routes, and programmatic SEO pages |
| **robots.txt** | Allows all crawlers; blocks `/api/`, `/account`, auth pages | | **robots.txt** | Allows all crawlers; blocks `/api/`, `/account`, auth pages |
| **Internationalization** | Full i18n in EN, AR, FR — all tool pages, SEO content, and static pages | | **Internationalization** | Full i18n in EN, AR, FR — all tool pages, SEO content, and static pages |
| **Font loading** | `dns-prefetch` + `preconnect` + `display=swap` for Google Fonts | | **Font loading** | `dns-prefetch` + `preconnect` + `display=swap` for Google Fonts |
@@ -58,7 +58,7 @@ VITE_GOOGLE_SITE_VERIFICATION=your-verification-code
## 3. SEO Content Architecture ## 3. SEO Content Architecture
### 3.1 Tool Landing Pages (32 pages) ### 3.1 Tool Landing Pages (44 direct tool routes + programmatic SEO pages)
Each tool page (`/tools/{slug}`) renders via `ToolLandingPage` wrapper: Each tool page (`/tools/{slug}`) renders via `ToolLandingPage` wrapper:
@@ -79,7 +79,7 @@ All text is i18n-driven from `seo.{toolKey}.*` keys in EN/AR/FR.
|------|--------|---------| |------|--------|---------|
| `/` | `WebSite` + `Organization` | Homepage with hero + tool grid | | `/` | `WebSite` + `Organization` | Homepage with hero + tool grid |
| `/about` | `WebPage` | Mission, technology, security | | `/about` | `WebPage` | Mission, technology, security |
| `/contact` | `WebPage` | Contact form (mailto-based) | | `/contact` | `WebPage` | Contact form backed by `/api/contact/submit` plus email fallback |
| `/privacy` | `WebPage` | Privacy policy | | `/privacy` | `WebPage` | Privacy policy |
| `/terms` | `WebPage` | Terms of service | | `/terms` | `WebPage` | Terms of service |
@@ -87,7 +87,7 @@ All text is i18n-driven from `seo.{toolKey}.*` keys in EN/AR/FR.
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `sitemap.xml` | All 37 indexable URLs with priority and changefreq | | `sitemap.xml` | Current public snapshot includes 245 indexable URLs with priority and changefreq values |
| `robots.txt` | Crawler directives + sitemap pointer | | `robots.txt` | Crawler directives + sitemap pointer |
| `llms.txt` | AI/LLM discoverability file | | `llms.txt` | AI/LLM discoverability file |
| `humans.txt` | Team credits | | `humans.txt` | Team credits |
@@ -106,7 +106,7 @@ All text is i18n-driven from `seo.{toolKey}.*` keys in EN/AR/FR.
- [ ] Verify Core Web Vitals pass (LCP < 2.5s, FID < 100ms, CLS < 0.1) - [ ] Verify Core Web Vitals pass (LCP < 2.5s, FID < 100ms, CLS < 0.1)
**Content:** **Content:**
- [ ] Publish all 32 tool pages with full SEO content - [ ] Publish and maintain the 44 direct tool pages with full SEO content
- [ ] Ensure hreflang tags work across EN/AR/FR (add `hreflang` links if using subdomains or subdirectories) - [ ] Ensure hreflang tags work across EN/AR/FR (add `hreflang` links if using subdomains or subdirectories)
- [ ] Add FAQ schema to all tool pages (already done) - [ ] Add FAQ schema to all tool pages (already done)
@@ -209,7 +209,7 @@ All text is i18n-driven from `seo.{toolKey}.*` keys in EN/AR/FR.
**Key differentiators for Dociva:** **Key differentiators for Dociva:**
1. **Trilingual** — EN/AR/FR from day one (Arabic market is largely unserved) 1. **Trilingual** — EN/AR/FR from day one (Arabic market is largely unserved)
2. **No signup** — zero friction, instant file processing 2. **No signup** — zero friction, instant file processing
3. **32 tools** — broader coverage than most competitors 3. **44 direct tool routes** — broad product coverage with room to surface more of the catalog on the homepage
4. **AI-powered tools** — OCR, Chat PDF, Summarize, Translate (unique value) 4. **AI-powered tools** — OCR, Chat PDF, Summarize, Translate (unique value)
5. **Privacy-first** — files auto-deleted within 30 minutes 5. **Privacy-first** — files auto-deleted within 30 minutes
@@ -219,7 +219,7 @@ All text is i18n-driven from `seo.{toolKey}.*` keys in EN/AR/FR.
``` ```
□ Review Search Console for crawl errors and fix immediately □ Review Search Console for crawl errors and fix immediately
□ Check index coverage — ensure all 37+ pages are indexed □ Check index coverage — ensure the current sitemap inventory is being indexed as expected
□ Review top queries — identify rising keywords to create content for □ Review top queries — identify rising keywords to create content for
□ Publish 816 blog posts (24/week × 3 languages) □ Publish 816 blog posts (24/week × 3 languages)
□ Build 510 backlinks through outreach □ Build 510 backlinks through outreach

View File

@@ -1,274 +1,328 @@
# Dociva — Tool Inventory & Competitive Gap Analysis # Dociva — Current Tool Inventory & Competitive Gap Analysis
> Generated: March 7, 2026 > Updated: March 25, 2026
> Branch: `feature/critical-maintenance-and-editor` > Source of truth: current code in `frontend/src/App.tsx`, `frontend/src/pages/HomePage.tsx`, `backend/app/routes`, and `backend/app/routes/v1/tools.py`
--- ---
## 1. Platform Infrastructure ## 1. Current Platform Snapshot
| Component | Technology | Status | | Area | Current state |
|---|---|---| |---|---|
| Backend | Flask + Gunicorn | ✅ Production-ready | | Backend | Flask + Gunicorn + Celery + Redis |
| Frontend | React + Vite + TypeScript + Tailwind | ✅ Production-ready | | Frontend | React + Vite + TypeScript + Tailwind |
| Task Queue | Celery + Redis | ✅ 3 queues (default, image, pdf_tools) | | Storage | Local filesystem + optional S3 |
| Scheduler | Celery Beat | ✅ Expired-file cleanup every 30 min | | Accounts | Session auth + usage history + API keys |
| Database | SQLite | ✅ Users, API keys, history, usage events | | Monetization | Ad slots + pricing page + Stripe subscription plumbing |
| Storage | Local + S3 (optional) | ✅ Presigned URLs | | API | `/api/v1/*` B2B surface for a significant subset of tools |
| Auth | Session-based + API Key (B2B) | ✅ Free & Pro plans | | i18n | English, Arabic, French |
| Security | Talisman CSP, rate limiting, CORS, input sanitization | ✅ | | SEO | Tool landing pages + structured data + generated public SEO files |
| i18n | react-i18next (en, ar, fr) | ✅ All tools translated |
| Monetization | Google AdSense slots | ✅ Integrated |
| Email | SMTP (password reset) | ✅ |
| Docker | docker-compose (dev + prod) | ✅ |
| Nginx | Reverse proxy + SSL | ✅ |
### Plans & Quotas ### Operational counts
| | Free | Pro |
|---|---|---|
| Web requests/month | 50 | 500 |
| API requests/month | — | 1,000 |
| Max file size | 50 MB | 100 MB |
| History retention | 25 | 250 |
| API key access | ❌ | ✅ |
### Registered Blueprints: 18
| Blueprint | Prefix | Purpose |
|---|---|---|
| `health_bp` | `/api` | Health check |
| `auth_bp` | `/api/auth` | Login, register, forgot/reset password |
| `account_bp` | `/api/account` | Profile, API keys, usage |
| `admin_bp` | `/api/internal/admin` | Plan management |
| `convert_bp` | `/api/convert` | PDF ↔ Word |
| `compress_bp` | `/api/compress` | PDF compression |
| `image_bp` | `/api/image` | Image convert & resize |
| `video_bp` | `/api/video` | Video to GIF |
| `history_bp` | `/api` | User history |
| `pdf_tools_bp` | `/api/pdf-tools` | Merge, split, rotate, watermark, etc. |
| `flowchart_bp` | `/api/flowchart` | AI flowchart extraction |
| `tasks_bp` | `/api/tasks` | Task status polling |
| `download_bp` | `/api/download` | Secure file download |
| `v1_bp` | `/api/v1` | B2B API (all tools) |
| `config_bp` | `/api/config` | Dynamic limits |
| `ocr_bp` | `/api/ocr` | OCR text extraction |
| `removebg_bp` | `/api/remove-bg` | Background removal |
| `pdf_editor_bp` | `/api/pdf-editor` | PDF text annotations |
---
## 2. Existing Tools — Complete Inventory (21 tools)
### 2.1 PDF Tools (14)
| # | Tool | Endpoint | Service | Task | Component | Route | i18n | B2B API |
|---|---|---|---|---|---|---|---|---|
| 1 | **Compress PDF** | `POST /api/compress/pdf` | `compress_service` | `compress_pdf_task` | `PdfCompressor.tsx` | `/tools/compress-pdf` | ✅ | ✅ |
| 2 | **PDF to Word** | `POST /api/convert/pdf-to-word` | `pdf_service` | `convert_pdf_to_word` | `PdfToWord.tsx` | `/tools/pdf-to-word` | ✅ | ✅ |
| 3 | **Word to PDF** | `POST /api/convert/word-to-pdf` | `pdf_service` | `convert_word_to_pdf` | `WordToPdf.tsx` | `/tools/word-to-pdf` | ✅ | ✅ |
| 4 | **Merge PDF** | `POST /api/pdf-tools/merge` | `pdf_tools_service` | `merge_pdfs_task` | `MergePdf.tsx` | `/tools/merge-pdf` | ✅ | ✅ |
| 5 | **Split PDF** | `POST /api/pdf-tools/split` | `pdf_tools_service` | `split_pdf_task` | `SplitPdf.tsx` | `/tools/split-pdf` | ✅ | ✅ |
| 6 | **Rotate PDF** | `POST /api/pdf-tools/rotate` | `pdf_tools_service` | `rotate_pdf_task` | `RotatePdf.tsx` | `/tools/rotate-pdf` | ✅ | ✅ |
| 7 | **PDF to Images** | `POST /api/pdf-tools/pdf-to-images` | `pdf_tools_service` | `pdf_to_images_task` | `PdfToImages.tsx` | `/tools/pdf-to-images` | ✅ | ✅ |
| 8 | **Images to PDF** | `POST /api/pdf-tools/images-to-pdf` | `pdf_tools_service` | `images_to_pdf_task` | `ImagesToPdf.tsx` | `/tools/images-to-pdf` | ✅ | ✅ |
| 9 | **Watermark PDF** | `POST /api/pdf-tools/watermark` | `pdf_tools_service` | `watermark_pdf_task` | `WatermarkPdf.tsx` | `/tools/watermark-pdf` | ✅ | ✅ |
| 10 | **Protect PDF** | `POST /api/pdf-tools/protect` | `pdf_tools_service` | `protect_pdf_task` | `ProtectPdf.tsx` | `/tools/protect-pdf` | ✅ | ✅ |
| 11 | **Unlock PDF** | `POST /api/pdf-tools/unlock` | `pdf_tools_service` | `unlock_pdf_task` | `UnlockPdf.tsx` | `/tools/unlock-pdf` | ✅ | ✅ |
| 12 | **Add Page Numbers** | `POST /api/pdf-tools/page-numbers` | `pdf_tools_service` | `add_page_numbers_task` | `AddPageNumbers.tsx` | `/tools/page-numbers` | ✅ | ✅ |
| 13 | **PDF Editor** | `POST /api/pdf-editor/edit` | `pdf_editor_service` | `edit_pdf_task` | `PdfEditor.tsx` | `/tools/pdf-editor` | ✅ | ❌ |
| 14 | **PDF Flowchart** | `POST /api/flowchart/extract` + 3 | `flowchart_service` | `extract_flowchart_task` | `PdfFlowchart.tsx` | `/tools/pdf-flowchart` | ✅ | ✅ |
### 2.2 Image Tools (4)
| # | Tool | Endpoint | Service | Task | Component | Route | i18n | B2B API |
|---|---|---|---|---|---|---|---|---|
| 15 | **Image Converter** | `POST /api/image/convert` | `image_service` | `convert_image_task` | `ImageConverter.tsx` | `/tools/image-converter` | ✅ | ✅ |
| 16 | **Image Resize** | `POST /api/image/resize` | `image_service` | `resize_image_task` | `ImageResize.tsx` | `/tools/image-resize` | ✅ | ✅ |
| 17 | **OCR** | `POST /api/ocr/image` + `/pdf` | `ocr_service` | `ocr_image_task` / `ocr_pdf_task` | `OcrTool.tsx` | `/tools/ocr` | ✅ | ❌ |
| 18 | **Remove Background** | `POST /api/remove-bg` | `removebg_service` | `remove_bg_task` | `RemoveBackground.tsx` | `/tools/remove-background` | ✅ | ❌ |
### 2.3 Video Tools (1)
| # | Tool | Endpoint | Service | Task | Component | Route | i18n | B2B API |
|---|---|---|---|---|---|---|---|---|
| 19 | **Video to GIF** | `POST /api/video/to-gif` | `video_service` | `create_gif_task` | `VideoToGif.tsx` | `/tools/video-to-gif` | ✅ | ✅ |
### 2.4 Text Tools — Client-Side Only (2)
| # | Tool | Backend | Component | Route | i18n |
|---|---|---|---|---|---|
| 20 | **Word Counter** | None (JS) | `WordCounter.tsx` | `/tools/word-counter` | ✅ |
| 21 | **Text Cleaner** | None (JS) | `TextCleaner.tsx` | `/tools/text-cleaner` | ✅ |
### Feature Flags
| Flag | Default | Controls |
|---|---|---|
| `FEATURE_EDITOR` | `false` | OCR, Remove Background, PDF Editor routes (403 when off) |
---
## 3. Test Coverage
| Category | Test Files | Tests |
|---|---|---|
| Auth | `test_auth.py` | 5 |
| Config | `test_config.py` | 3 |
| Password reset | `test_password_reset.py` | 8 |
| Maintenance | `test_maintenance_tasks.py` | 8 |
| Compress | `test_compress.py`, `test_compress_service.py`, `test_compress_tasks.py` | 6 |
| Convert | `test_convert.py`, `test_convert_tasks.py` | 6 |
| Image | `test_image.py`, `test_image_service.py`, `test_image_tasks.py` | ~18 |
| Video | `test_video.py`, `test_video_service.py`, `test_video_tasks.py` | ~12 |
| PDF tools | `test_pdf_tools.py`, `test_pdf_tools_service.py`, `test_pdf_tools_tasks.py` | ~50 |
| Flowchart | `test_flowchart_tasks.py` | ~6 |
| OCR | `test_ocr.py`, `test_ocr_service.py` | 12 |
| Remove BG | `test_removebg.py` | 3 |
| PDF Editor | `test_pdf_editor.py` | 7 |
| Infra | `test_download.py`, `test_health.py`, `test_history.py`, `test_rate_limiter.py`, `test_sanitizer.py`, `test_storage_service.py`, `test_file_validator.py`, `test_utils.py`, `test_tasks_route.py` | ~36 |
| **TOTAL** | **30 files** | **180 ✅** |
---
## 4. Missing Tools — Competitive Gap Analysis
Comparison against: iLovePDF, SmallPDF, TinyWow, PDF24, Adobe Acrobat Online.
### 4.1 HIGH PRIORITY — Core tools competitors all have
| # | Tool | Category | Complexity | Dependencies | Notes |
|---|---|---|---|---|---|
| 1 | **Compress Image** | Image | Low | Pillow (exists) | JPEG/PNG/WebP quality reduction + resize. Pillow already installed. |
| 2 | **PDF to Excel** | PDF → Office | Medium | `camelot-py` or `tabula-py` | Table extraction from PDFs — high user demand. |
| 3 | **PDF to PowerPoint** | PDF → Office | Medium | `python-pptx` | Convert PDF pages to PPTX slides (images per slide or OCR). |
| 4 | **Excel to PDF** | Office → PDF | Medium | LibreOffice CLI | Same pattern as Word to PDF. |
| 5 | **PowerPoint to PDF** | Office → PDF | Medium | LibreOffice CLI | Same pattern as Word to PDF. |
| 6 | **HTML to PDF** | Web → PDF | Low | `weasyprint` or `playwright` | Input URL or HTML snippet → PDF. |
| 7 | **Reorder / Rearrange Pages** | PDF | Low | PyPDF2 (exists) | Drag-and-drop page reorder UI → backend rebuilds PDF. |
| 8 | **Extract Pages** | PDF | Low | PyPDF2 (exists) | Similar to Split but with visual page picker. Already partially covered by Split tool. |
| 9 | **Sign PDF** | PDF | Medium | ReportLab + canvas | Draw/upload signature → overlay onto PDF page. |
| 10 | **PDF Repair** | PDF | Low | PyPDF2 (exists) | Read → rewrite to fix broken xref tables. |
### 4.2 MEDIUM PRIORITY — Differentiators present on 23 competitors
| # | Tool | Category | Complexity | Dependencies | Notes |
|---|---|---|---|---|---|
| 11 | **PDF to PDF/A** | PDF | Medium | Ghostscript (exists) | Archival format conversion. |
| 12 | **Flatten PDF** | PDF | Low | PyPDF2 (exists) | Remove form fields / annotations → flat page. |
| 13 | **Crop PDF** | PDF | Medium | PyPDF2 (exists) | Crop margins / adjust page boundaries. |
| 14 | **Compare PDFs** | PDF | High | `diff-match-patch` + PyPDF2 | Side-by-side visual diff of two documents. |
| 15 | **QR Code Generator** | Utility | Low | `qrcode` + Pillow | Text/URL → QR image. Client-side possible but backend for API. |
| 16 | **Barcode Generator** | Utility | Low | `python-barcode` | Generate Code128, EAN, UPC barcodes. |
| 17 | **Image Crop** | Image | Low | Pillow (exists) | Visual cropping UI → backend Pillow crop. |
| 18 | **Image Rotate / Flip** | Image | Low | Pillow (exists) | 90°/180°/270° + horizontal/vertical flip. |
| 19 | **Image Filters** | Image | Low | Pillow (exists) | Grayscale, sepia, blur, sharpen, brightness, contrast. |
### 4.3 LOW PRIORITY — Advanced / niche (12 competitors, premium features)
| # | Tool | Category | Complexity | Dependencies | Notes |
|---|---|---|---|---|---|
| 20 | **AI Chat with PDF** | AI | High | OpenRouter (exists) | Upload PDF → ask questions. Flowchart service has partial foundation. |
| 21 | **AI PDF Summarizer** | AI | Medium | OpenRouter (exists) | Extract text → prompt LLM for summary. |
| 22 | **AI PDF Translator** | AI | Medium | OpenRouter (exists) | Extract text → translate via LLM → overlay or return translated doc. |
| 23 | **PDF Form Filler** | PDF | High | ReportLab + PyPDF2 | Detect form fields → UI to fill → save. |
| 24 | **Redact PDF** | PDF | Medium | ReportLab + PyPDF2 | Blackout sensitive text regions. |
| 25 | **PDF Metadata Editor** | PDF | Low | PyPDF2 (exists) | Edit title, author, subject, keywords. |
| 26 | **eSign / Digital Signature** | PDF | High | `cryptography` + PKCS#7 | Cryptographic digital signatures (different from visual sign). |
| 27 | **Batch Processing** | All | Medium | Existing tasks | Upload multiple files → apply same operation to all. |
| 28 | **GIF to Video** | Video | Medium | ffmpeg (exists) | Reverse of Video to GIF. |
| 29 | **Video Compress** | Video | Medium | ffmpeg (exists) | Reduce video file size. |
| 30 | **Audio Extract** | Video | Low | ffmpeg (exists) | Extract audio track from video → MP3/WAV. |
| 31 | **Screenshot to PDF** | Utility | Low | Pillow (exists) | Paste screenshot → generate PDF (similar to Images to PDF). |
| 32 | **Markdown to PDF** | Utility | Low | `markdown` + WeasyPrint | Render Markdown → PDF. |
| 33 | **JSON / CSV Viewer** | Utility | Low | Client-side | Pretty-print structured data. |
---
## 5. Implementation Readiness Matrix
Tools grouped by effort required (backend dependencies already present in the project):
### Ready to build (dependencies exist: PyPDF2, Pillow, Ghostscript, ffmpeg)
| Tool | Effort | Reuses |
|---|---|---|
| Compress Image | ~2h | `image_service.py` + Pillow |
| Reorder Pages | ~3h | `pdf_tools_service.py` + PyPDF2 |
| Extract Pages | ~2h | Split tool pattern |
| PDF Repair | ~2h | PyPDF2 read/write |
| Flatten PDF | ~2h | PyPDF2 |
| Crop PDF | ~3h | PyPDF2 MediaBox |
| Image Crop | ~2h | Pillow |
| Image Rotate/Flip | ~2h | Pillow |
| Image Filters | ~3h | Pillow ImageFilter |
| PDF Metadata Editor | ~2h | PyPDF2 |
| PDF to PDF/A | ~2h | Ghostscript (exists in Dockerfile) |
| QR Code Generator | ~2h | `qrcode` pip package |
| AI PDF Summarizer | ~3h | `ai_chat_service.py` + OpenRouter |
| GIF to Video | ~2h | ffmpeg |
| Audio Extract | ~2h | ffmpeg |
### Need new dependencies (1 pip package)
| Tool | New Dependency | Effort |
|---|---|---|
| PDF to Excel | `camelot-py[cv]` or `tabula-py` | ~4h |
| PDF to PowerPoint | `python-pptx` | ~4h |
| Excel to PDF | LibreOffice CLI (exists) | ~3h |
| PowerPoint to PDF | LibreOffice CLI (exists) | ~3h |
| HTML to PDF | `weasyprint` or `playwright` | ~4h |
| Sign PDF | ReportLab (exists) + canvas overlay | ~6h |
| Barcode Generator | `python-barcode` | ~2h |
| Markdown to PDF | `markdown` + `weasyprint` | ~3h |
### Requires significant new architecture
| Tool | Complexity | Effort |
|---|---|---|
| AI Chat with PDF | RAG pipeline or full-doc prompt | ~8h |
| AI PDF Translator | OCR + LLM + overlay | ~8h |
| PDF Form Filler | Field detection + fill engine | ~10h |
| Redact PDF | Region detection + blackout overlay | ~6h |
| Compare PDFs | Diff algorithm + visual rendering | ~10h |
| eSign / Digital Signature | PKCS#7 cryptographic signing | ~10h |
| Batch Processing | Queue orchestration for multi-file | ~6h |
| Video Compress | ffmpeg transcoding | ~4h |
---
## 6. Summary
| Metric | Count | | Metric | Count |
|---|---| |---|---|
| **Existing tools** | 21 | | Registered blueprints | 33 |
| **Missing HIGH priority** | 10 | | Backend route modules | 33 |
| **Missing MEDIUM priority** | 9 | | Backend service modules | 32 |
| **Missing LOW priority** | 14 | | Backend task modules | 20 |
| **Total gap** | 33 | | Frontend tool components | 44 |
| **Backend tests** | 180 ✅ | | Direct frontend tool routes | 44 |
| **Frontend build** | ✅ Clean | | Homepage tool cards surfaced | 33 |
| **Blueprints** | 18 | | Backend test files | 38 |
| **Celery task modules** | 10 | | Frontend test files | 6 |
| **Service files** | 15 |
| **i18n languages** | 3 (en, ar, fr) |
### Competitor Parity Score ### Plans & quotas
| Competitor | Their tools | We match | Coverage | From `backend/app/services/policy_service.py`:
|---|---|---|---|
| iLovePDF | ~25 core | ~16 | 64% |
| SmallPDF | ~21 core | ~15 | 71% |
| TinyWow | ~50+ (many AI) | ~14 | 28% |
| PDF24 | ~30 core | ~17 | 57% |
### Recommended Next Sprint | Plan | Web requests/month | API requests/month | History retention | Effective upload limits |
|---|---:|---:|---:|---|
| Free | 50 | — | 25 | Base file-size limits from backend config |
| Pro | 500 | 1,000 | 250 | 2x backend file-size limits |
**Highest ROI — 6 tools to reach 80%+ parity with SmallPDF/iLovePDF:** ---
1. Compress Image (Pillow — already installed) ## 2. Current Tool Inventory — 44 Direct Tool Routes
2. PDF to Excel (`camelot-py`)
3. HTML to PDF (`weasyprint`) These are the currently wired tool routes in `frontend/src/App.tsx`.
4. Sign PDF (ReportLab overlay)
5. Reorder Pages (PyPDF2 — already installed) ### 2.1 Core PDF Tools (14)
6. PDF to PowerPoint (`python-pptx`)
1. Compress PDF
2. PDF to Word
3. Word to PDF
4. Merge PDF
5. Split PDF
6. Rotate PDF
7. PDF to Images
8. Images to PDF
9. Watermark PDF
10. Protect PDF
11. Unlock PDF
12. Add Page Numbers
13. PDF Editor
14. PDF Flowchart
### 2.2 Image Tools (6)
15. Image Converter
16. Image Resize
17. Compress Image
18. OCR
19. Remove Background
20. Image to SVG
### 2.3 Conversion Tools (2)
21. PDF to Excel
22. HTML to PDF
### 2.4 PDF Extra Tools (3)
23. Remove Watermark
24. Reorder PDF
25. Extract Pages
### 2.5 AI Tools (4)
26. Chat with PDF
27. Summarize PDF
28. Translate PDF
29. Extract Tables
### 2.6 Utility / Other Tools (1)
30. QR Code Generator
### 2.7 Video Tools (1)
31. Video to GIF
### 2.8 Text Tools (2)
32. Word Counter
33. Text Cleaner
### 2.9 Phase 2 — PDF Conversion (4)
34. PDF to PowerPoint
35. Excel to PDF
36. PowerPoint to PDF
37. Sign PDF
### 2.10 Phase 2 — PDF Extra (4)
38. Crop PDF
39. Flatten PDF
40. Repair PDF
41. PDF Metadata Editor
### 2.11 Phase 2 — Image & Utility (3)
42. Image Crop
43. Image Rotate / Flip
44. Barcode Generator
---
## 3. Homepage Coverage vs Full Catalog
The codebase exposes **44** tool routes, but the homepage currently surfaces **33** tools.
### Surfaced on homepage
The homepage includes the core catalog and several growth-focused tools, including:
- Core PDF tools
- Core image tools
- OCR / remove background
- PDF to Excel
- HTML to PDF
- Chat / summarize / translate / table extraction
- QR code
- Video to GIF
- Word counter / text cleaner
### Implemented but not surfaced on homepage
The following routes exist but are not currently represented on the homepage tool grid:
- PDF to PowerPoint
- Excel to PDF
- PowerPoint to PDF
- Sign PDF
- Crop PDF
- Flatten PDF
- Repair PDF
- PDF Metadata Editor
- Image Crop
- Image Rotate / Flip
- Barcode Generator
This is an important product and growth gap: implementation breadth is currently ahead of homepage discovery.
---
## 4. Backend / API Coverage
### Web app coverage
The web app has direct frontend routes for all 44 tools listed above.
### B2B API coverage
`backend/app/routes/v1/tools.py` exposes a substantial subset of the platform through `/api/v1/*`, including:
- PDF conversion and compression
- Major PDF tools
- Image conversion and resize
- Video to GIF
- OCR
- Remove background
- AI PDF workflows
- PDF to Excel
- HTML to PDF
- QR and barcode generation
- PDF to PowerPoint / Excel to PDF / PowerPoint to PDF
- Sign PDF
- Crop / flatten / repair / metadata
- Image crop / image rotate-flip
This means the platform is no longer only a consumer-facing tools site; it already has real API-product potential.
---
## 5. Test & Quality Snapshot
### Backend
- `38` backend test files currently exist under `backend/tests`
- Sample backend verification run on March 25, 2026:
- `backend/tests/test_file_validator.py`
- `backend/tests/test_html_to_pdf.py`
- `backend/tests/test_auth.py`
- `backend/tests/test_history.py`
- Result: `24/24` tests passed
### Frontend
- `6` frontend test files currently exist under `frontend/src`
- Verified on March 25, 2026:
- Result: `64/64` tests passed
### Build
- Frontend production build passes successfully
---
## 6. What Is No Longer Missing
The earlier inventory marked many of the following as gaps. They are now implemented in the current codebase:
- Compress Image
- PDF to Excel
- PDF to PowerPoint
- Excel to PDF
- PowerPoint to PDF
- HTML to PDF
- Reorder PDF Pages
- Extract Pages
- Sign PDF
- PDF Repair
- Flatten PDF
- Crop PDF
- QR Code Generator
- Barcode Generator
- Image Crop
- Image Rotate / Flip
- AI Chat with PDF
- PDF Summarizer
- PDF Translator
- Table Extractor
- PDF Metadata Editor
---
## 7. Remaining Competitive Gaps
Compared against iLovePDF, Smallpdf, PDF24, TinyWow, and Adobe Acrobat online flows, the main remaining gaps are now more focused.
### High-value remaining gaps
1. PDF comparison / diff
2. PDF to PDF/A
3. Batch processing workflows
4. True cryptographic digital signature / eSign
5. PDF form filler
6. PDF redaction
7. Video compression
8. Audio extraction from video
9. GIF to video
10. Image filters / adjustments
### Strategic gaps, not just feature gaps
1. Homepage/catalog mismatch
2. Documentation drift
3. Full production validation for monetization and analytics
4. Sharper positioning for the strongest tool clusters
---
## 8. Competitive Position Today
### Where Dociva is strong
- Unusually broad tool coverage for an early-stage codebase
- Trilingual product surface from the start
- Real SaaS primitives already in place: plans, quotas, billing, API keys
- Async processing architecture suitable for file workflows
- Stronger-than-average SEO foundation for this stage
### Where Dociva is still weaker than category leaders
- Brand trust and distribution
- Workflow polish on every tool
- Product discovery consistency
- Team/business collaboration features
- Proof of production traction
---
## 9. Recommended Next Sprint
### Product quality
1. Keep frontend tests green as part of CI
2. Run a broader backend validation pass
3. Audit hidden tools and decide whether to surface or intentionally defer them
### Documentation
1. Keep this file as the current source of truth for tool counts
2. Update project status and SEO docs whenever route counts or sitemap scale change
### Growth
1. Focus on 8 high-intent landing pages first:
- Compress PDF
- PDF to Word
- Word to PDF
- Merge PDF
- PDF to Excel
- OCR
- Remove Background
- HTML to PDF
2. Improve conversion from free use to account creation to Pro/API
---
## 10. Bottom Line
The project is no longer a 21-tool MVP. It is currently a **44-route document-processing platform** with a real SaaS foundation and a growing competitive surface.
The core challenge has shifted:
> The next bottleneck is no longer raw feature count. It is consistency, positioning, discovery, and turning breadth into measurable growth.

View File

@@ -225,6 +225,6 @@ describe('useTaskPolling', () => {
await vi.runAllTimersAsync(); await vi.runAllTimersAsync();
}); });
expect(result.current.error).toBe('Task failed.'); expect(result.current.error).toBe('Processing failed. Please try again.');
}); });
}); });

View File

@@ -5,6 +5,9 @@ import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import InternalAdminPage from './InternalAdminPage'; import InternalAdminPage from './InternalAdminPage';
import { useAuthStore } from '@/stores/authStore'; import { useAuthStore } from '@/stores/authStore';
import { import {
getAdminPlanInterest,
getAdminSystemHealth,
getAdminUserStats,
getInternalAdminContacts, getInternalAdminContacts,
getInternalAdminOverview, getInternalAdminOverview,
listInternalAdminUsers, listInternalAdminUsers,
@@ -18,6 +21,9 @@ vi.mock('@/stores/authStore', () => ({
})); }));
vi.mock('@/services/api', () => ({ vi.mock('@/services/api', () => ({
getAdminPlanInterest: vi.fn(),
getAdminSystemHealth: vi.fn(),
getAdminUserStats: vi.fn(),
getInternalAdminContacts: vi.fn(), getInternalAdminContacts: vi.fn(),
getInternalAdminOverview: vi.fn(), getInternalAdminOverview: vi.fn(),
listInternalAdminUsers: vi.fn(), listInternalAdminUsers: vi.fn(),
@@ -37,7 +43,7 @@ const authState = {
function renderPage() { function renderPage() {
return render( return render(
<HelmetProvider> <HelmetProvider>
<MemoryRouter> <MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
<InternalAdminPage /> <InternalAdminPage />
</MemoryRouter> </MemoryRouter>
</HelmetProvider> </HelmetProvider>
@@ -56,11 +62,49 @@ describe('InternalAdminPage', () => {
(selector: (state: typeof authState) => unknown) => selector(authState) (selector: (state: typeof authState) => unknown) => selector(authState)
); );
(getInternalAdminOverview as Mock).mockReset(); (getInternalAdminOverview as Mock).mockReset();
(getAdminSystemHealth as Mock).mockReset();
(getAdminPlanInterest as Mock).mockReset();
(getAdminUserStats as Mock).mockReset();
(listInternalAdminUsers as Mock).mockReset(); (listInternalAdminUsers as Mock).mockReset();
(getInternalAdminContacts as Mock).mockReset(); (getInternalAdminContacts as Mock).mockReset();
(markInternalAdminContactRead as Mock).mockReset(); (markInternalAdminContactRead as Mock).mockReset();
(updateInternalAdminUserPlan as Mock).mockReset(); (updateInternalAdminUserPlan as Mock).mockReset();
(updateInternalAdminUserRole as Mock).mockReset(); (updateInternalAdminUserRole as Mock).mockReset();
(getAdminSystemHealth as Mock).mockResolvedValue({
ai_configured: true,
ai_model: 'nvidia/nemotron-3-super-120b-a12b:free',
ai_budget_used_percent: 25,
error_rate_1h: 0,
tasks_last_1h: 0,
failures_last_1h: 0,
database_size_mb: 1.5,
});
(getAdminPlanInterest as Mock).mockResolvedValue({
total_clicks: 0,
unique_users: 0,
clicks_last_7d: 0,
clicks_last_30d: 0,
by_plan: [],
recent: [],
});
(getAdminUserStats as Mock).mockResolvedValue({
total_users: 2,
new_last_7d: 1,
new_last_30d: 2,
pro_users: 1,
free_users: 1,
daily_registrations: [{ day: '2026-03-16', count: 1 }],
most_active_users: [
{
id: 2,
email: 'operator@example.com',
plan: 'free',
created_at: '2026-03-16T10:00:00Z',
total_tasks: 3,
},
],
});
}); });
it('shows the admin sign-in form for anonymous users', () => { it('shows the admin sign-in form for anonymous users', () => {
@@ -129,10 +173,16 @@ describe('InternalAdminPage', () => {
renderPage(); renderPage();
await waitFor(() => { await waitFor(() => {
expect(screen.getByText('Users and monetization')).toBeTruthy(); expect(screen.getByText('Top tools')).toBeTruthy();
}); });
fireEvent.click(screen.getByRole('button', { name: 'Set admin' })); fireEvent.click(screen.getByRole('button', { name: 'Users' }));
await waitFor(() => {
expect(screen.getByText('User management')).toBeTruthy();
});
fireEvent.click(screen.getByRole('button', { name: 'Admin' }));
await waitFor(() => { await waitFor(() => {
expect(updateInternalAdminUserRole).toHaveBeenCalledWith(2, 'admin'); expect(updateInternalAdminUserRole).toHaveBeenCalledWith(2, 'admin');

View File

@@ -29,22 +29,6 @@
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
/* تحسين تحميل الخطوط لتقليل CLS */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: local('Inter'), url('/fonts/Inter.woff2') format('woff2');
font-display: swap;
}
@font-face {
font-family: 'Tajawal';
font-style: normal;
font-weight: 400;
src: local('Tajawal'), url('/fonts/Tajawal.woff2') format('woff2');
font-display: swap;
}
/* RTL Support */ /* RTL Support */
[dir="rtl"] body { [dir="rtl"] body {
font-family: 'Tajawal', 'Inter', system-ui, sans-serif; font-family: 'Tajawal', 'Inter', system-ui, sans-serif;

View File

@@ -0,0 +1,6 @@
import '@testing-library/jest-dom/vitest';
import '@/i18n';
if (typeof window !== 'undefined') {
window.localStorage.setItem('i18nextLng', 'en');
}

View File

@@ -24,6 +24,7 @@ export default defineConfig({
test: { test: {
globals: true, globals: true,
environment: 'jsdom', environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
}, },
resolve: { resolve: {
alias: { alias: {