feat: add frontend import UI (refs #26)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-11 19:58:17 -06:00
parent 068db991a4
commit 7a5579df7b
7 changed files with 712 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
/**
* @ai-summary Import button component
* @ai-context Opens file selector and triggers import dialog
*/
import React, { useRef } from 'react';
import toast from 'react-hot-toast';
interface ImportButtonProps {
onFileSelected: (file: File) => void;
disabled?: boolean;
}
export const ImportButton: React.FC<ImportButtonProps> = ({
onFileSelected,
disabled = false,
}) => {
const fileInputRef = useRef<HTMLInputElement>(null);
const handleButtonClick = () => {
fileInputRef.current?.click();
};
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
// Validate file extension
if (!file.name.endsWith('.tar.gz')) {
toast.error('Please select a .tar.gz file');
return;
}
// Validate file size (max 500MB)
const maxSize = 500 * 1024 * 1024;
if (file.size > maxSize) {
toast.error('File size exceeds 500MB limit');
return;
}
onFileSelected(file);
// Reset input so same file can be selected again
event.target.value = '';
};
return (
<>
<input
ref={fileInputRef}
type="file"
accept=".tar.gz"
onChange={handleFileChange}
style={{ display: 'none' }}
aria-label="Select import file"
/>
<button
onClick={handleButtonClick}
disabled={disabled}
className="w-full text-left p-3 bg-primary-50 text-primary-700 rounded-lg font-medium hover:bg-primary-100 transition-colors disabled:opacity-50 dark:bg-primary-900/20 dark:text-primary-300 dark:hover:bg-primary-900/30"
style={{ minHeight: '44px' }}
>
Import My Data
</button>
</>
);
};