Files
quasar/frontend/src/components/games/ImportSheet.tsx
Benito Rodríguez a07096d7c7
Some checks failed
CI / lint (push) Failing after 1m5s
CI / test-backend (push) Has been skipped
CI / test-frontend (push) Has been skipped
CI / test-e2e (push) Has been skipped
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.
2026-03-18 19:21:36 +01:00

173 lines
5.3 KiB
TypeScript

'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>
);
}