refactor(admin): split evidence, import, portfolio pages
Extract components and hooks from oversized pages into colocated _components/ and _hooks/ subdirectories to enforce the 500-LOC hard cap. page.tsx files reduced to 205, 121, and 136 LOC respectively. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
100
admin-compliance/app/sdk/import/_components/UploadZone.tsx
Normal file
100
admin-compliance/app/sdk/import/_components/UploadZone.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useCallback } from 'react'
|
||||
|
||||
export function UploadZone({
|
||||
onFilesAdded,
|
||||
isDisabled,
|
||||
}: {
|
||||
onFilesAdded: (files: File[]) => void
|
||||
isDisabled: boolean
|
||||
}) {
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
|
||||
const handleDragOver = useCallback((e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
if (!isDisabled) setIsDragging(true)
|
||||
}, [isDisabled])
|
||||
|
||||
const handleDragLeave = useCallback((e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
setIsDragging(false)
|
||||
}, [])
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
setIsDragging(false)
|
||||
if (isDisabled) return
|
||||
const files = Array.from(e.dataTransfer.files).filter(
|
||||
f => f.type === 'application/pdf' || f.type.startsWith('image/')
|
||||
)
|
||||
if (files.length > 0) onFilesAdded(files)
|
||||
},
|
||||
[onFilesAdded, isDisabled]
|
||||
)
|
||||
|
||||
const handleFileSelect = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files && !isDisabled) {
|
||||
onFilesAdded(Array.from(e.target.files))
|
||||
}
|
||||
},
|
||||
[onFilesAdded, isDisabled]
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
className={`relative border-2 border-dashed rounded-xl p-12 text-center transition-all ${
|
||||
isDisabled
|
||||
? 'border-gray-200 bg-gray-50 cursor-not-allowed'
|
||||
: isDragging
|
||||
? 'border-purple-500 bg-purple-50'
|
||||
: 'border-gray-300 hover:border-purple-400 hover:bg-purple-50/50 cursor-pointer'
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf,image/*"
|
||||
multiple
|
||||
onChange={handleFileSelect}
|
||||
disabled={isDisabled}
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer disabled:cursor-not-allowed"
|
||||
/>
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className={`w-16 h-16 rounded-full flex items-center justify-center ${isDragging ? 'bg-purple-100' : 'bg-gray-100'}`}>
|
||||
<svg
|
||||
className={`w-8 h-8 ${isDragging ? 'text-purple-600' : 'text-gray-400'}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-lg font-medium text-gray-900">
|
||||
{isDragging ? 'Dateien hier ablegen' : 'Dokumente hochladen'}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Ziehen Sie PDF-Dateien hierher oder klicken Sie zum Auswaehlen
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-gray-400">
|
||||
<span>Unterstuetzte Formate:</span>
|
||||
<span className="px-2 py-0.5 bg-gray-100 rounded">PDF</span>
|
||||
<span className="px-2 py-0.5 bg-gray-100 rounded">JPG</span>
|
||||
<span className="px-2 py-0.5 bg-gray-100 rounded">PNG</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user