diff --git a/.github/workflows/video-backend-ci.yml b/.github/workflows/video-backend-ci.yml
index 718856a4..7f1a037a 100644
--- a/.github/workflows/video-backend-ci.yml
+++ b/.github/workflows/video-backend-ci.yml
@@ -15,25 +15,32 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v4
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 10
+
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- cache: 'npm'
+ cache: 'pnpm'
- name: Install Dependencies
- run: npm ci
+ run: pnpm install --frozen-lockfile
- name: Run Prisma Generate
- run: npx prisma generate
+ run: pnpm exec prisma generate
+ env:
+ DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/postgres"
- - name: ESLint Check
- run: npm run lint
+ - name: Type Check
+ run: pnpm exec tsc --noEmit
- name: Run Vitest Suite (Unit & Integration)
- run: npm run test
+ run: pnpm run test
- name: Build Next.js 15
- run: npm run build
+ run: pnpm run build
env:
NEXT_TELEMETRY_DISABLED: 1
diff --git a/src/modules/pdf-forge/components/PdfForgeDashboard.tsx b/src/modules/pdf-forge/components/PdfForgeDashboard.tsx
index 138a0825..6cc5e53a 100644
--- a/src/modules/pdf-forge/components/PdfForgeDashboard.tsx
+++ b/src/modules/pdf-forge/components/PdfForgeDashboard.tsx
@@ -2,14 +2,13 @@
import React from 'react';
import Link from 'next/link';
import { motion } from 'framer-motion';
-import * as Icons from 'lucide-react';
import { PDF_TOOLS } from '../constants/tools';
export default function PdfForgeDashboard() {
return (
{PDF_TOOLS.map((tool, i) => {
- const IconComponent = (Icons as unknown as Record>)[tool.icon] || Icons.FileText;
+ const IconComponent = tool.icon;
return (
;
gradient: string;
engines: PdfEngine[];
execution: PdfExecution;
@@ -13,34 +46,34 @@ export interface PdfToolDef {
}
export const PDF_TOOLS: PdfToolDef[] = [
- { id: 'merge', name: 'PDF Merger', description: 'Gabungkan beberapa file PDF menjadi satu dokumen', icon: 'FileStack', gradient: 'from-red-500 to-orange-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/merger', acceptMultiple: true },
- { id: 'split', name: 'PDF Splitter', description: 'Pecah PDF berdasarkan halaman atau range', icon: 'Scissors', gradient: 'from-orange-500 to-amber-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/splitter', acceptMultiple: false },
- { id: 'compress', name: 'PDF Compressor', description: 'Kompres ukuran PDF dengan optimasi gambar', icon: 'Minimize2', gradient: 'from-amber-500 to-yellow-500', engines: ['pdf-lib', 'sharp'], execution: 'server', route: '/pdf/compressor', acceptMultiple: false },
- { id: 'convert', name: 'PDF Converter', description: 'Konversi dari/ke format Office via LibreOffice', icon: 'RefreshCw', gradient: 'from-yellow-500 to-lime-500', engines: ['libreoffice'], execution: 'server', route: '/pdf/converter', acceptMultiple: false },
- { id: 'ocr', name: 'PDF OCR', description: 'Ekstrak teks dari PDF scan via Tesseract WASM', icon: 'ScanText', gradient: 'from-lime-500 to-green-500', engines: ['tesseract'], execution: 'client', route: '/pdf/ocr', acceptMultiple: false },
- { id: 'edit', name: 'PDF Editor', description: 'Edit teks dan elemen PDF secara langsung', icon: 'PenTool', gradient: 'from-green-500 to-emerald-500', engines: ['pdfjs', 'pdf-lib'], execution: 'client', route: '/pdf/editor', acceptMultiple: false },
- { id: 'annotate', name: 'PDF Annotator', description: 'Tambah highlight, catatan, dan gambar di atas PDF', icon: 'Highlighter', gradient: 'from-emerald-500 to-teal-500', engines: ['pdfjs', 'konva'], execution: 'client', route: '/pdf/annotator', acceptMultiple: false },
- { id: 'watermark', name: 'Watermark Tool', description: 'Tambah watermark teks/gambar ke setiap halaman', icon: 'Stamp', gradient: 'from-teal-500 to-cyan-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/watermark', acceptMultiple: false },
- { id: 'digital-signature', name: 'Digital Signature', description: 'Tanda tangan digital PAdES via Web Crypto', icon: 'ShieldCheck', gradient: 'from-cyan-500 to-sky-500', engines: ['web-crypto', 'pdf-lib'], execution: 'client', route: '/pdf/digital-signature', acceptMultiple: false },
- { id: 'encrypt', name: 'PDF Encrypt', description: 'Enkripsi PDF dengan AES-256 password', icon: 'Lock', gradient: 'from-sky-500 to-blue-500', engines: ['web-crypto'], execution: 'client', route: '/pdf/encrypt', acceptMultiple: false },
- { id: 'decrypt', name: 'PDF Decrypt', description: 'Buka PDF terenkripsi dengan password', icon: 'Unlock', gradient: 'from-blue-500 to-indigo-500', engines: ['web-crypto'], execution: 'client', route: '/pdf/decrypt', acceptMultiple: false },
- { id: 'rotate-pages', name: 'Rotate Pages', description: 'Rotasi halaman 90°/180°/270°', icon: 'RotateCw', gradient: 'from-indigo-500 to-violet-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/rotate-pages', acceptMultiple: false },
- { id: 'crop-pages', name: 'Crop Pages', description: 'Potong area halaman dengan cropBox', icon: 'Crop', gradient: 'from-violet-500 to-purple-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/crop-pages', acceptMultiple: false },
- { id: 'extract-pages', name: 'Extract Pages', description: 'Ekstrak halaman tertentu ke PDF baru', icon: 'FileOutput', gradient: 'from-purple-500 to-fuchsia-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/extract-pages', acceptMultiple: false },
- { id: 'reorder-pages', name: 'Reorder Pages', description: 'Susun ulang halaman dengan drag-and-drop', icon: 'ArrowUpDown', gradient: 'from-fuchsia-500 to-pink-500', engines: ['pdf-lib', 'dnd-kit'], execution: 'client', route: '/pdf/reorder-pages', acceptMultiple: false },
- { id: 'redact', name: 'PDF Redactor', description: 'Sensor informasi sensitif secara permanen', icon: 'EyeOff', gradient: 'from-pink-500 to-rose-500', engines: ['pdfjs', 'pdf-lib'], execution: 'client', route: '/pdf/redactor', acceptMultiple: false },
- { id: 'form-fill', name: 'Form Filler', description: 'Isi formulir AcroForm PDF interaktif', icon: 'ClipboardEdit', gradient: 'from-rose-500 to-red-600', engines: ['pdfjs'], execution: 'client', route: '/pdf/form-filler', acceptMultiple: false },
- { id: 'to-word', name: 'PDF to Word', description: 'Konversi PDF ke DOCX via LibreOffice', icon: 'FileText', gradient: 'from-blue-600 to-blue-800', engines: ['libreoffice', 'mammoth'], execution: 'server', route: '/pdf/to-word', acceptMultiple: false },
- { id: 'to-excel', name: 'PDF to Excel', description: 'Konversi tabel PDF ke XLSX', icon: 'Table', gradient: 'from-green-600 to-green-800', engines: ['pdf2json', 'libreoffice'], execution: 'server', route: '/pdf/to-excel', acceptMultiple: false },
- { id: 'to-powerpoint', name: 'PDF to PowerPoint', description: 'Konversi PDF ke PPTX via LibreOffice', icon: 'Presentation', gradient: 'from-orange-600 to-orange-800', engines: ['libreoffice'], execution: 'server', route: '/pdf/to-powerpoint', acceptMultiple: false },
- { id: 'to-image', name: 'PDF to Image', description: 'Render halaman PDF ke PNG/JPEG', icon: 'Image', gradient: 'from-purple-600 to-purple-800', engines: ['pdfjs', 'sharp'], execution: 'client', route: '/pdf/to-image', acceptMultiple: false },
- { id: 'from-image', name: 'Image to PDF', description: 'Gabung gambar menjadi satu PDF', icon: 'ImagePlus', gradient: 'from-teal-600 to-teal-800', engines: ['pdf-lib', 'heic2any'], execution: 'client', route: '/pdf/from-image', acceptMultiple: true },
- { id: 'from-html', name: 'HTML to PDF', description: 'Konversi HTML/URL ke PDF via Puppeteer', icon: 'Globe', gradient: 'from-slate-600 to-slate-800', engines: ['puppeteer'], execution: 'server', route: '/pdf/from-html', acceptMultiple: false },
- { id: 'metadata-edit', name: 'Metadata Editor', description: 'Edit title, author, subject, keywords', icon: 'Info', gradient: 'from-gray-500 to-gray-700', engines: ['pdf-lib'], execution: 'client', route: '/pdf/metadata-editor', acceptMultiple: false },
- { id: 'table-extract', name: 'Table Extractor', description: 'Ekstrak tabel terstruktur dari PDF', icon: 'Grid3X3', gradient: 'from-emerald-600 to-emerald-800', engines: ['pdf2json'], execution: 'server', route: '/pdf/table-extractor', acceptMultiple: false },
- { id: 'repair', name: 'PDF Repair', description: 'Perbaiki PDF rusak via XREF rebuild', icon: 'Wrench', gradient: 'from-amber-600 to-amber-800', engines: ['pdf-lib'], execution: 'client', route: '/pdf/repair', acceptMultiple: false },
- { id: 'compare', name: 'PDF Compare', description: 'Bandingkan dua PDF dan highlight perbedaan', icon: 'GitCompare', gradient: 'from-cyan-600 to-cyan-800', engines: ['pdf-parse', 'diff-match-patch'], execution: 'hybrid', route: '/pdf/compare', acceptMultiple: true },
- { id: 'batch-process', name: 'Batch Processor', description: 'Proses multiple PDF sekaligus dengan antrian', icon: 'Layers', gradient: 'from-violet-600 to-violet-800', engines: ['pdf-lib'], execution: 'client', route: '/pdf/batch-processor', acceptMultiple: true },
- { id: 'bookmarks-edit', name: 'Bookmarks Editor', description: 'Edit outline/bookmarks PDF', icon: 'Bookmark', gradient: 'from-red-600 to-red-800', engines: ['pdf-lib'], execution: 'client', route: '/pdf/bookmarks-editor', acceptMultiple: false },
- { id: 'xref-analyze', name: 'XREF Analyzer', description: 'Analisis struktur internal XREF table PDF', icon: 'Binary', gradient: 'from-zinc-500 to-zinc-700', engines: ['pdf-lib', 'pdf-parse'], execution: 'client', route: '/pdf/xref-analyzer', acceptMultiple: false },
+ { id: 'merge', name: 'PDF Merger', description: 'Gabungkan beberapa file PDF menjadi satu dokumen', icon: FileStack, gradient: 'from-red-500 to-orange-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/merger', acceptMultiple: true },
+ { id: 'split', name: 'PDF Splitter', description: 'Pecah PDF berdasarkan halaman atau range', icon: Scissors, gradient: 'from-orange-500 to-amber-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/splitter', acceptMultiple: false },
+ { id: 'compress', name: 'PDF Compressor', description: 'Kompres ukuran PDF dengan optimasi gambar', icon: Minimize2, gradient: 'from-amber-500 to-yellow-500', engines: ['pdf-lib', 'sharp'], execution: 'server', route: '/pdf/compressor', acceptMultiple: false },
+ { id: 'convert', name: 'PDF Converter', description: 'Konversi dari/ke format Office via LibreOffice', icon: RefreshCw, gradient: 'from-yellow-500 to-lime-500', engines: ['libreoffice'], execution: 'server', route: '/pdf/converter', acceptMultiple: false },
+ { id: 'ocr', name: 'PDF OCR', description: 'Ekstrak teks dari PDF scan via Tesseract WASM', icon: ScanText, gradient: 'from-lime-500 to-green-500', engines: ['tesseract'], execution: 'client', route: '/pdf/ocr', acceptMultiple: false },
+ { id: 'edit', name: 'PDF Editor', description: 'Edit teks dan elemen PDF secara langsung', icon: PenTool, gradient: 'from-green-500 to-emerald-500', engines: ['pdfjs', 'pdf-lib'], execution: 'client', route: '/pdf/editor', acceptMultiple: false },
+ { id: 'annotate', name: 'PDF Annotator', description: 'Tambah highlight, catatan, dan gambar di atas PDF', icon: Highlighter, gradient: 'from-emerald-500 to-teal-500', engines: ['pdfjs', 'konva'], execution: 'client', route: '/pdf/annotator', acceptMultiple: false },
+ { id: 'watermark', name: 'Watermark Tool', description: 'Tambah watermark teks/gambar ke setiap halaman', icon: Stamp, gradient: 'from-teal-500 to-cyan-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/watermark', acceptMultiple: false },
+ { id: 'digital-signature', name: 'Digital Signature', description: 'Tanda tangan digital PAdES via Web Crypto', icon: ShieldCheck, gradient: 'from-cyan-500 to-sky-500', engines: ['web-crypto', 'pdf-lib'], execution: 'client', route: '/pdf/digital-signature', acceptMultiple: false },
+ { id: 'encrypt', name: 'PDF Encrypt', description: 'Enkripsi PDF dengan AES-256 password', icon: Lock, gradient: 'from-sky-500 to-blue-500', engines: ['web-crypto'], execution: 'client', route: '/pdf/encrypt', acceptMultiple: false },
+ { id: 'decrypt', name: 'PDF Decrypt', description: 'Buka PDF terenkripsi dengan password', icon: Unlock, gradient: 'from-blue-500 to-indigo-500', engines: ['web-crypto'], execution: 'client', route: '/pdf/decrypt', acceptMultiple: false },
+ { id: 'rotate-pages', name: 'Rotate Pages', description: 'Rotasi halaman 90°/180°/270°', icon: RotateCw, gradient: 'from-indigo-500 to-violet-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/rotate-pages', acceptMultiple: false },
+ { id: 'crop-pages', name: 'Crop Pages', description: 'Potong area halaman dengan cropBox', icon: Crop, gradient: 'from-violet-500 to-purple-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/crop-pages', acceptMultiple: false },
+ { id: 'extract-pages', name: 'Extract Pages', description: 'Ekstrak halaman tertentu ke PDF baru', icon: FileOutput, gradient: 'from-purple-500 to-fuchsia-500', engines: ['pdf-lib'], execution: 'client', route: '/pdf/extract-pages', acceptMultiple: false },
+ { id: 'reorder-pages', name: 'Reorder Pages', description: 'Susun ulang halaman dengan drag-and-drop', icon: ArrowUpDown, gradient: 'from-fuchsia-500 to-pink-500', engines: ['pdf-lib', 'dnd-kit'], execution: 'client', route: '/pdf/reorder-pages', acceptMultiple: false },
+ { id: 'redact', name: 'PDF Redactor', description: 'Sensor informasi sensitif secara permanen', icon: EyeOff, gradient: 'from-pink-500 to-rose-500', engines: ['pdfjs', 'pdf-lib'], execution: 'client', route: '/pdf/redactor', acceptMultiple: false },
+ { id: 'form-fill', name: 'Form Filler', description: 'Isi formulir AcroForm PDF interaktif', icon: ClipboardEdit, gradient: 'from-rose-500 to-red-600', engines: ['pdfjs'], execution: 'client', route: '/pdf/form-filler', acceptMultiple: false },
+ { id: 'to-word', name: 'PDF to Word', description: 'Konversi PDF ke DOCX via LibreOffice', icon: FileText, gradient: 'from-blue-600 to-blue-800', engines: ['libreoffice', 'mammoth'], execution: 'server', route: '/pdf/to-word', acceptMultiple: false },
+ { id: 'to-excel', name: 'PDF to Excel', description: 'Konversi tabel PDF ke XLSX', icon: Table, gradient: 'from-green-600 to-green-800', engines: ['pdf2json', 'libreoffice'], execution: 'server', route: '/pdf/to-excel', acceptMultiple: false },
+ { id: 'to-powerpoint', name: 'PDF to PowerPoint', description: 'Konversi PDF ke PPTX via LibreOffice', icon: Presentation, gradient: 'from-orange-600 to-orange-800', engines: ['libreoffice'], execution: 'server', route: '/pdf/to-powerpoint', acceptMultiple: false },
+ { id: 'to-image', name: 'PDF to Image', description: 'Render halaman PDF ke PNG/JPEG', icon: ImageIcon, gradient: 'from-purple-600 to-purple-800', engines: ['pdfjs', 'sharp'], execution: 'client', route: '/pdf/to-image', acceptMultiple: false },
+ { id: 'from-image', name: 'Image to PDF', description: 'Gabung gambar menjadi satu PDF', icon: ImagePlus, gradient: 'from-teal-600 to-teal-800', engines: ['pdf-lib', 'heic2any'], execution: 'client', route: '/pdf/from-image', acceptMultiple: true },
+ { id: 'from-html', name: 'HTML to PDF', description: 'Konversi HTML/URL ke PDF via Puppeteer', icon: Globe, gradient: 'from-slate-600 to-slate-800', engines: ['puppeteer'], execution: 'server', route: '/pdf/from-html', acceptMultiple: false },
+ { id: 'metadata-edit', name: 'Metadata Editor', description: 'Edit title, author, subject, keywords', icon: Info, gradient: 'from-gray-500 to-gray-700', engines: ['pdf-lib'], execution: 'client', route: '/pdf/metadata-editor', acceptMultiple: false },
+ { id: 'table-extract', name: 'Table Extractor', description: 'Ekstrak tabel terstruktur dari PDF', icon: Grid3X3, gradient: 'from-emerald-600 to-emerald-800', engines: ['pdf2json'], execution: 'server', route: '/pdf/table-extractor', acceptMultiple: false },
+ { id: 'repair', name: 'PDF Repair', description: 'Perbaiki PDF rusak via XREF rebuild', icon: Wrench, gradient: 'from-amber-600 to-amber-800', engines: ['pdf-lib'], execution: 'client', route: '/pdf/repair', acceptMultiple: false },
+ { id: 'compare', name: 'PDF Compare', description: 'Bandingkan dua PDF dan highlight perbedaan', icon: GitCompare, gradient: 'from-cyan-600 to-cyan-800', engines: ['pdf-parse', 'diff-match-patch'], execution: 'hybrid', route: '/pdf/compare', acceptMultiple: true },
+ { id: 'batch-process', name: 'Batch Processor', description: 'Proses multiple PDF sekaligus dengan antrian', icon: Layers, gradient: 'from-violet-600 to-violet-800', engines: ['pdf-lib'], execution: 'client', route: '/pdf/batch-processor', acceptMultiple: true },
+ { id: 'bookmarks-edit', name: 'Bookmarks Editor', description: 'Edit outline/bookmarks PDF', icon: Bookmark, gradient: 'from-red-600 to-red-800', engines: ['pdf-lib'], execution: 'client', route: '/pdf/bookmarks-editor', acceptMultiple: false },
+ { id: 'xref-analyze', name: 'XREF Analyzer', description: 'Analisis struktur internal XREF table PDF', icon: Binary, gradient: 'from-zinc-500 to-zinc-700', engines: ['pdf-lib', 'pdf-parse'], execution: 'client', route: '/pdf/xref-analyzer', acceptMultiple: false },
];