feat: add sidebar, characters page, and equipments page with filter
- Add `react-router-dom` for client-side routing. - Add `shadcn` components: `Sidebar`, `Dialog`, `Button`, `ScrollArea`, `Avatar`, `Badge`, `Card`, `Sheet`, `Tooltip`, `Separator`, `Input`, `Label`. - Implement `AppSidebar` with navigation to Characters and Equipments. - Implement `Characters` page displaying character avatars. - Implement `Equipments` page with: - Horizontal scrollable category filter (multi-select). - Equipment grid layout. - "Extract Equipment" button opening a `Dialog` (refactored from `Extractor` component, maintaining original UI design). - Added support for pasting images directly into the Extractor Dialog. - Update `Equipment` API and Service to support filtering by multiple categories. - Add database index to `Equipment` table on `[category, subCategory]`. - Refactor `App.tsx` and `Layout.tsx` to use the new routing and sidebar structure. - Remove legacy `Stash` and `Extractor` components.
This commit is contained in:
1 parent
6991642f69
commit
7f837ead71
1 file changed
+26
-6
@@ -18,6 +18,8 @@ interface ExtractorDialogProps {
|
||||
onSuccess: (assets: ExtractedAsset[]) => void
|
||||
}
|
||||
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const ExtractorDialog: React.FC<ExtractorDialogProps> = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
@@ -30,17 +32,35 @@ export const ExtractorDialog: React.FC<ExtractorDialogProps> = ({
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [results, setResults] = useState<ExtractedAsset[]>([])
|
||||
|
||||
const processFile = (f: File) => {
|
||||
setFile(f)
|
||||
setPreview(URL.createObjectURL(f))
|
||||
setResults([])
|
||||
setError(null)
|
||||
setStatus('idle')
|
||||
}
|
||||
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
const f = e.target.files[0]
|
||||
setFile(f)
|
||||
setPreview(URL.createObjectURL(f))
|
||||
setResults([])
|
||||
setError(null)
|
||||
setStatus('idle')
|
||||
processFile(e.target.files[0])
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const handlePaste = (e: ClipboardEvent) => {
|
||||
if (!open) return
|
||||
if (e.clipboardData && e.clipboardData.files.length > 0) {
|
||||
const f = e.clipboardData.files[0]
|
||||
if (f.type.startsWith('image/')) {
|
||||
processFile(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('paste', handlePaste)
|
||||
return () => window.removeEventListener('paste', handlePaste)
|
||||
}, [open])
|
||||
|
||||
const handleExtract = async () => {
|
||||
if (!file) return
|
||||
|
||||
|
||||
Reference in new issue
Block a user