image scanning and asset edit
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
ToggleButton, Button, CircularProgress, TablePagination, Typography, Chip, Tooltip,
|
||||
} from '@mui/material';
|
||||
import { adminService } from '../../api/adminService';
|
||||
import type { MissingMediaRecord, MissingMediaSummary } from '../../api/adminService';
|
||||
import type { MissingMediaRecord, MissingMediaSummary, MissingMediaFilter } from '../../api/adminService';
|
||||
import { accountService } from '../../api/accountService';
|
||||
import { useToastStore } from '../../store/toastStore';
|
||||
|
||||
@@ -27,6 +27,7 @@ export const MissingMedia: React.FC = () => {
|
||||
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
||||
const [dbName, setDbName] = useState('');
|
||||
const [type, setType] = useState<'audits' | 'rectification'>('audits');
|
||||
const [missing, setMissing] = useState<'all' | MissingMediaFilter>('all');
|
||||
const [records, setRecords] = useState<MissingMediaRecord[]>([]);
|
||||
const [summary, setSummary] = useState<MissingMediaSummary | null>(null);
|
||||
const [total, setTotal] = useState(0);
|
||||
@@ -53,23 +54,27 @@ export const MissingMedia: React.FC = () => {
|
||||
try {
|
||||
const result = await adminService.getMissingMedia({
|
||||
dbName, type, limit: PAGE_SIZE, offset: page * PAGE_SIZE,
|
||||
...(missing !== 'all' ? { missing } : {}),
|
||||
});
|
||||
setRecords(result.records);
|
||||
setTotal(result.total);
|
||||
setSummary(result.summary);
|
||||
// A recheck under an active filter can shrink the filtered total below
|
||||
// the current page - snap back instead of stranding on an empty page.
|
||||
if (page > 0 && result.total <= page * PAGE_SIZE) setPage(0);
|
||||
} catch {
|
||||
useToastStore.getState().showToast('Failed to load missing-media records.', 'error');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [dbName, type, page]);
|
||||
}, [dbName, type, page, missing]);
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
|
||||
useEffect(() => {
|
||||
setPage(0);
|
||||
setSelectedIds(new Set());
|
||||
}, [dbName, type]);
|
||||
}, [dbName, type, missing]);
|
||||
|
||||
const toggleRow = (id: number) => {
|
||||
setSelectedIds((prev) => {
|
||||
@@ -119,6 +124,13 @@ export const MissingMedia: React.FC = () => {
|
||||
<ToggleButton value="rectification">Rectification</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
|
||||
<ToggleButtonGroup value={missing} exclusive onChange={(_, v) => v && setMissing(v)} size="small">
|
||||
<ToggleButton value="all">All</ToggleButton>
|
||||
<ToggleButton value="image">Image missing</ToggleButton>
|
||||
<ToggleButton value="video">Video missing</ToggleButton>
|
||||
<ToggleButton value="both">Both missing</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={selectedIds.size === 0 || selectedIds.size > 50 || rechecking}
|
||||
@@ -136,6 +148,7 @@ export const MissingMedia: React.FC = () => {
|
||||
{summaryChip('Not checked yet', summary?.neverChecked, 'default')}
|
||||
{summaryChip('Image missing', summary?.missingImage, 'error')}
|
||||
{summaryChip('Video missing', summary?.missingVideo, 'warning')}
|
||||
{summaryChip('Both missing', summary?.missingBoth, 'error')}
|
||||
{summaryChip('Gave up (24h of retries)', summary?.gaveUp, 'info')}
|
||||
</Box>
|
||||
|
||||
@@ -167,7 +180,9 @@ export const MissingMedia: React.FC = () => {
|
||||
{records.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={9} align="center" sx={{ color: 'text.secondary' }}>
|
||||
No pending records with missing media — everything is auditable. 🎉
|
||||
{missing !== 'all'
|
||||
? 'No records match this filter.'
|
||||
: 'No pending records with missing media — everything is auditable. 🎉'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user