feat: add UI components for alert dialog, badge, checkbox, dialog, label, select, sheet, table, textarea
- Implemented AlertDialog component with overlay, content, header, footer, title, description, action, and cancel functionalities. - Created Badge component with variant support for different styles. - Developed Checkbox component with custom styling and indicator. - Added Dialog component with trigger, close, overlay, content, header, footer, title, and description. - Introduced Label component for form elements. - Built Select component with trigger, content, group, item, label, separator, and scroll buttons. - Created Sheet component with trigger, close, overlay, content, header, footer, title, and description. - Implemented Table component with header, body, footer, row, head, cell, and caption. - Added Textarea component with custom styling. - Established API service for game management with CRUD operations and metadata search functionalities. - Updated dependencies in package lock files.
This commit is contained in:
172
frontend/src/components/games/ImportSheet.tsx
Normal file
172
frontend/src/components/games/ImportSheet.tsx
Normal file
@@ -0,0 +1,172 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { ImportRequest, ImportResult, importApi } from '@/lib/api';
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from '@/components/ui/sheet';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { UploadIcon, FolderOpenIcon, CheckCircleIcon, XCircleIcon, LoaderIcon } from 'lucide-react';
|
||||
|
||||
interface ImportSheetProps {
|
||||
onSuccess: () => void;
|
||||
}
|
||||
|
||||
export function ImportSheet({ onSuccess }: ImportSheetProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [directory, setDirectory] = useState('');
|
||||
const [recursive, setRecursive] = useState(true);
|
||||
const [isImporting, setIsImporting] = useState(false);
|
||||
const [result, setResult] = useState<ImportResult | null>(null);
|
||||
|
||||
const handleImport = async () => {
|
||||
if (!directory.trim()) return;
|
||||
|
||||
setIsImporting(true);
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const importData: ImportRequest = {
|
||||
directory: directory.trim(),
|
||||
recursive,
|
||||
};
|
||||
|
||||
const importResult = await importApi.start(importData);
|
||||
setResult(importResult);
|
||||
|
||||
if (importResult.success) {
|
||||
onSuccess();
|
||||
}
|
||||
} catch (err) {
|
||||
setResult({
|
||||
success: false,
|
||||
message: err instanceof Error ? err.message : 'Error al importar',
|
||||
imported: 0,
|
||||
errors: [err instanceof Error ? err.message : 'Error desconocido'],
|
||||
});
|
||||
} finally {
|
||||
setIsImporting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
setDirectory('');
|
||||
setResult(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<Button>
|
||||
<UploadIcon data-icon="inline-start" />
|
||||
Importar Juegos
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="sm:max-w-md">
|
||||
<SheetHeader>
|
||||
<SheetTitle>Importar Juegos</SheetTitle>
|
||||
<SheetDescription>
|
||||
Importa juegos desde archivos ROM en un directorio local.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="flex flex-col gap-4 py-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="directory">Directorio</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="directory"
|
||||
value={directory}
|
||||
onChange={(e) => setDirectory(e.target.value)}
|
||||
placeholder="/path/to/roms"
|
||||
disabled={isImporting}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
disabled={isImporting}
|
||||
title="Seleccionar directorio"
|
||||
>
|
||||
<FolderOpenIcon className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="recursive"
|
||||
checked={recursive}
|
||||
onCheckedChange={(checked) => setRecursive(checked === true)}
|
||||
disabled={isImporting}
|
||||
/>
|
||||
<Label htmlFor="recursive" className="cursor-pointer">
|
||||
Incluir subdirectorios
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{result && (
|
||||
<div className="rounded-lg border border-border p-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
{result.success ? (
|
||||
<CheckCircleIcon className="size-5 text-emerald-500" />
|
||||
) : (
|
||||
<XCircleIcon className="size-5 text-destructive" />
|
||||
)}
|
||||
<span className="font-medium">
|
||||
{result.success ? 'Importación completada' : 'Error en la importación'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mb-2">{result.message}</p>
|
||||
<p className="text-sm font-medium">ROMs importadas: {result.imported}</p>
|
||||
{result.errors.length > 0 && (
|
||||
<div className="mt-3">
|
||||
<p className="text-sm font-medium mb-1">Errores:</p>
|
||||
<ul className="text-sm text-destructive list-disc list-inside">
|
||||
{result.errors.map((error, index) => (
|
||||
<li key={index}>{error}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isImporting && (
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<LoaderIcon className="size-4 animate-spin" />
|
||||
<span>Importando ROMs...</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-2 mt-auto">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1"
|
||||
onClick={handleClose}
|
||||
disabled={isImporting}
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button
|
||||
className="flex-1"
|
||||
onClick={handleImport}
|
||||
disabled={isImporting || !directory.trim()}
|
||||
>
|
||||
{isImporting ? 'Importando...' : 'Importar'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user