diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 8154990..5614c25 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -97,6 +97,12 @@ jobs: container: image: python:3.12-slim steps: + - name: Install Node.js for checkout action + run: | + apt-get update && apt-get install -y curl + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs + - name: Check out code uses: actions/checkout@v4 @@ -147,6 +153,12 @@ jobs: DATABASE_URL_AUTH: postgresql+psycopg2://postgres:postgres@postgres:5432/punimtag_auth_test REDIS_URL: redis://redis:6379/0 steps: + - name: Install Node.js for checkout action + run: | + apt-get update && apt-get install -y curl + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs + - name: Check out code uses: actions/checkout@v4 diff --git a/admin-frontend/src/components/PhotoViewer.tsx b/admin-frontend/src/components/PhotoViewer.tsx index 1eec0ac..25d535b 100644 --- a/admin-frontend/src/components/PhotoViewer.tsx +++ b/admin-frontend/src/components/PhotoViewer.tsx @@ -36,7 +36,7 @@ export default function PhotoViewer({ photos, initialIndex, onClose }: PhotoView // Slideshow state const [isPlaying, setIsPlaying] = useState(false) const [slideshowInterval, setSlideshowInterval] = useState(3) // seconds - const slideshowTimerRef = useRef(null) + const slideshowTimerRef = useRef | null>(null) // Favorite state const [isFavorite, setIsFavorite] = useState(false) diff --git a/admin-frontend/src/pages/Modify.tsx b/admin-frontend/src/pages/Modify.tsx index e198bfc..3bc33a2 100644 --- a/admin-frontend/src/pages/Modify.tsx +++ b/admin-frontend/src/pages/Modify.tsx @@ -547,6 +547,33 @@ export default function Modify() { }) } + const confirmUnmatchFace = async () => { + if (!unmatchConfirmDialog || !selectedPersonId || !unmatchConfirmDialog.faceId) return + + try { + setBusy(true) + setError(null) + setUnmatchConfirmDialog(null) + + // Unmatch the single face + await facesApi.batchUnmatch({ face_ids: [unmatchConfirmDialog.faceId] }) + + // Reload people list to update face counts + const peopleRes = await peopleApi.listWithFaces(lastNameFilter || undefined) + setPeople(peopleRes.items) + + // Reload faces + await loadPersonFaces(selectedPersonId) + + setSuccess('Successfully unlinked face') + setTimeout(() => setSuccess(null), 3000) + } catch (err: any) { + setError(err.response?.data?.detail || err.message || 'Failed to unmatch face') + } finally { + setBusy(false) + } + } + const confirmBulkUnmatchFaces = async () => { if (!unmatchConfirmDialog || !selectedPersonId || selectedFaces.size === 0) return diff --git a/admin-frontend/src/pages/Tags.tsx b/admin-frontend/src/pages/Tags.tsx index da9c734..73af192 100644 --- a/admin-frontend/src/pages/Tags.tsx +++ b/admin-frontend/src/pages/Tags.tsx @@ -414,7 +414,9 @@ export default function Tags() { } } - // Save pending changes + // Save pending changes (currently unused, kept for future use) + // @ts-expect-error - Intentionally unused, kept for future use + // eslint-disable-next-line @typescript-eslint/no-unused-vars const _saveChanges = async () => { const pendingPhotoIds = new Set([ ...Object.keys(pendingTagChanges).map(Number), @@ -483,7 +485,9 @@ export default function Tags() { } } - // Get pending changes count + // Get pending changes count (currently unused, kept for future use) + // @ts-expect-error - Intentionally unused, kept for future use + // eslint-disable-next-line @typescript-eslint/no-unused-vars const _pendingChangesCount = useMemo(() => { const additions = Object.values(pendingTagChanges).reduce((sum, ids) => sum + ids.length, 0) const removals = Object.values(pendingTagRemovals).reduce((sum, ids) => sum + ids.length, 0) @@ -1549,7 +1553,7 @@ function PhotoTagDialog({ // Bulk Tag Dialog Component function BulkTagDialog({ - folderPath, + folderPath: _folderPath, folder, tags, pendingTagChanges, @@ -1559,7 +1563,7 @@ function BulkTagDialog({ onRemoveTag, getPhotoTags, }: { - folderPath: string // eslint-disable-line @typescript-eslint/no-unused-vars + folderPath: string folder: FolderGroup | undefined tags: TagResponse[] pendingTagChanges: Record