From d2b067735cb94180bb48895bdcb45e270e76cdc9 Mon Sep 17 00:00:00 2001 From: RaymondVerhoef Date: Mon, 18 May 2026 22:03:10 +0200 Subject: [PATCH] feat: add UploadZone component for drag-and-drop file processing --- src/components/admin/UploadZone.jsx | 190 +--------------------------- 1 file changed, 2 insertions(+), 188 deletions(-) diff --git a/src/components/admin/UploadZone.jsx b/src/components/admin/UploadZone.jsx index c372fb3..ac4519e 100644 --- a/src/components/admin/UploadZone.jsx +++ b/src/components/admin/UploadZone.jsx @@ -1,23 +1,10 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { UploadCloud, AlertCircle, CheckCircle, RefreshCw, GitBranch, FileText, ArrowUpCircle, MinusCircle } from 'lucide-react'; +import React, { useState, useRef } from 'react'; +import { UploadCloud, AlertCircle, CheckCircle } from 'lucide-react'; import { processSourceText } from '../../lib/extractionPipeline'; -import { getRepoFolder, getFileContent, parseGitHubUrl } from '../../lib/giteaService'; import * as db from '../../lib/db'; import Card from '../ui/Card'; import Button from '../ui/Button'; -// ── File status badge ───────────────────────────────────────────────────────── - -const StatusBadge = ({ status }) => { - if (status === 'new') return New; - if (status === 'updated') return Updated; - if (status === 'current') return Up to date; - if (status === 'done') return Processed; - if (status === 'error') return Failed; - if (status === 'skipped') return Skipped; - return null; -}; - // ── Main Component ──────────────────────────────────────────────────────────── const UploadZone = ({ onUploadComplete }) => { @@ -25,21 +12,8 @@ const UploadZone = ({ onUploadComplete }) => { const [isProcessing, setIsProcessing] = useState(false); const [status, setStatus] = useState(null); - // GitHub sync state - const [githubUrl, setGithubUrl] = useState('https://github.com/respellion/employee-handbook/tree/main/docs/knowledge-base'); - const [isFetching, setIsFetching] = useState(false); - const [fileList, setFileList] = useState(null); - const [syncProgress, setSyncProgress] = useState(null); - const fileInputRef = useRef(null); - // Load GitHub URL from PocketBase settings on mount - useEffect(() => { - db.getSetting('github:url', '').then(url => { - if (url) setGithubUrl(url); - }); - }, []); - // ── File upload (drag & drop / browse) ──────────────────────────────────── const processFile = async (file) => { @@ -77,88 +51,6 @@ const UploadZone = ({ onUploadComplete }) => { if (e.target.files?.length > 0) processFile(e.target.files[0]); }; - // ── Gitea Sync ───────────────────────────────────────────────────────────── - - const handleFetchFiles = async () => { - const parsed = parseGitHubUrl(githubUrl); - if (!parsed) { - setStatus({ type: 'error', msg: 'Enter a valid GitHub folder URL (e.g. https://github.com/owner/repo/tree/main/docs).' }); - return; - } - setIsFetching(true); - setFileList(null); - setStatus(null); - try { - const files = await getRepoFolder(parsed.owner, parsed.repo, parsed.folder, parsed.branch); - const enriched = await Promise.all(files.map(async (f) => { - const storedSha = await db.getSetting(`github:sha:${f.name}`, null); - let syncStatus = 'new'; - if (storedSha === f.sha) syncStatus = 'current'; - else if (storedSha && storedSha !== f.sha) syncStatus = 'updated'; - return { ...f, syncStatus, _parsed: parsed }; - })); - setFileList(enriched); - // Persist URL for next visit - await db.setSetting('github:url', githubUrl.trim()); - } catch (err) { - setStatus({ type: 'error', msg: `Could not fetch from GitHub: ${err.message}` }); - } finally { - setIsFetching(false); - } - }; - - const handleSync = async () => { - const toProcess = fileList.filter(f => f.syncStatus === 'new' || f.syncStatus === 'updated'); - if (toProcess.length === 0) { - setStatus({ type: 'success', msg: 'Everything is already up to date.' }); - return; - } - - setIsProcessing(true); - setSyncProgress({ current: 0, total: toProcess.length }); - let processed = 0, failed = 0; - - // Update a single file's status in the list - const updateFileStatus = (filename, newStatus) => { - setFileList(prev => prev.map(f => f.name === filename ? { ...f, syncStatus: newStatus } : f)); - }; - - for (const file of toProcess) { - const { _parsed: p } = file; - try { - const content = await getFileContent(p.owner, p.repo, file.path, p.branch); - - // If this is an update, delete the old source so deduplication allows re-processing - if (file.syncStatus === 'updated') { - const existing = await db.getSources(); - const old = existing.find(s => s.name === file.name && s.status === 'completed'); - if (old) await db.deleteSource(old.id); - } - - await processSourceText(content, file.name); - await db.setSetting(`github:sha:${file.name}`, file.sha); - updateFileStatus(file.name, 'done'); - processed++; - } catch (err) { - console.error(`[GitHub Sync] Failed: ${file.name}`, err); - updateFileStatus(file.name, 'error'); - failed++; - } - setSyncProgress(p => ({ ...p, current: p.current + 1 })); - } - - setSyncProgress(null); - setIsProcessing(false); - if (onUploadComplete) onUploadComplete(); - - const parts = []; - if (processed > 0) parts.push(`${processed} file${processed > 1 ? 's' : ''} processed`); - if (failed > 0) parts.push(`${failed} failed`); - setStatus({ type: failed > 0 ? 'error' : 'success', msg: `Sync complete: ${parts.join(', ')}.` }); - }; - - const toProcess = fileList ? fileList.filter(f => f.syncStatus === 'new' || f.syncStatus === 'updated') : []; - // ── Render ───────────────────────────────────────────────────────────────── return ( @@ -189,84 +81,6 @@ const UploadZone = ({ onUploadComplete }) => { /> - {/* ─── Divider ─── */} -
-
- OR -
-
- - {/* ─── GitHub Sync Panel ─── */} - -
-
-
- -
-

GitHub Sync

-
- setGithubUrl(e.target.value)} - placeholder="https://github.com/owner/repo/tree/main/folder" - className="flex-1 h-9 px-3 rounded-[var(--r-sm)] border border-bg-warm bg-bg text-sm focus:outline-none focus:ring-2 focus:ring-teal/30 focus:border-teal" - /> -
-
-
- {fileList && toProcess.length > 0 && ( - - )} - -
-
- - - {/* File list */} - {fileList && ( -
- {fileList.length === 0 ? ( -

No .md or .txt files found in this folder.

- ) : fileList.map(file => ( -
-
- - {file.name} -
- -
- ))} -
- )} - - {/* Progress bar */} - {syncProgress && ( -
-
-
-
-

- Processing {syncProgress.current} of {syncProgress.total} files… -

-
- )} - - {/* ─── Status messages ─── */} {status && (