diff --git a/admin-frontend/package-lock.json b/admin-frontend/package-lock.json index ffb4527..a4d4e65 100644 --- a/admin-frontend/package-lock.json +++ b/admin-frontend/package-lock.json @@ -11,6 +11,7 @@ "@tanstack/react-query": "^5.8.4", "axios": "^1.6.2", "exifr": "^7.1.3", + "lucide-react": "^0.553.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.20.0" @@ -4599,6 +4600,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.553.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.553.0.tgz", + "integrity": "sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", diff --git a/admin-frontend/package.json b/admin-frontend/package.json index a2bffea..8a07c72 100644 --- a/admin-frontend/package.json +++ b/admin-frontend/package.json @@ -14,6 +14,7 @@ "@tanstack/react-query": "^5.8.4", "axios": "^1.6.2", "exifr": "^7.1.3", + "lucide-react": "^0.553.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.20.0" diff --git a/admin-frontend/src/pages/ApproveIdentified.tsx b/admin-frontend/src/pages/ApproveIdentified.tsx index 0fbfe07..22e39cf 100644 --- a/admin-frontend/src/pages/ApproveIdentified.tsx +++ b/admin-frontend/src/pages/ApproveIdentified.tsx @@ -171,9 +171,9 @@ export default function ApproveIdentified() { }) const message = [ - `✅ Approved: ${response.approved}`, - `❌ Denied: ${response.denied}`, - response.errors.length > 0 ? `⚠️ Errors: ${response.errors.length}` : '' + `Approved: ${response.approved}`, + `Denied: ${response.denied}`, + response.errors.length > 0 ? `Errors: ${response.errors.length}` : '' ].filter(Boolean).join('\n') showToast(message, response.errors.length > 0 ? 'error' : 'success') @@ -247,8 +247,8 @@ export default function ApproveIdentified() { const response = await pendingIdentificationsApi.clearDenied() const message = [ - `✅ Deleted ${response.deleted_records} denied record(s)`, - response.errors.length > 0 ? `⚠️ Errors: ${response.errors.length}` : '' + `Deleted ${response.deleted_records} denied record(s)`, + response.errors.length > 0 ? `Errors: ${response.errors.length}` : '' ].filter(Boolean).join('\n') showToast(message, response.errors.length > 0 ? 'error' : 'success') diff --git a/admin-frontend/src/pages/Help.tsx b/admin-frontend/src/pages/Help.tsx index 9814781..975c6a4 100644 --- a/admin-frontend/src/pages/Help.tsx +++ b/admin-frontend/src/pages/Help.tsx @@ -1,4 +1,22 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, type ReactNode } from 'react' +import type { LucideIcon } from 'lucide-react' +import { + ArrowLeft, + Bot, + ChevronRight, + Flag, + FolderInput, + Pencil, + Search, + Settings, + Tag, + Tags, + Upload, + User, + UserCheck, + Users, + Wrench, +} from 'lucide-react' type PageId = 'overview' | 'scan' | 'process' | 'identify' | 'auto-match' | 'search' | 'modify' | 'tags' | 'faces-maintenance' | 'user-identified-faces' | 'user-reported-photos' | 'user-tagged-photos' | 'user-uploaded-photos' | 'users' @@ -69,74 +87,75 @@ export default function Help() { ) } +type HelpNavItem = { + id: PageId + icon: LucideIcon + label: string + description: string +} + +function HelpNavButton({ item, onPageClick }: { item: HelpNavItem; onPageClick: (page: PageId) => void }) { + const Icon = item.icon + return ( + + ) +} + function NavigationOverview({ onPageClick }: { onPageClick: (page: PageId) => void }) { - const mainNavItems = [ - { id: 'scan' as PageId, icon: '🗂️', label: 'Scan', description: 'Import photos from folders or upload files' }, - { id: 'process' as PageId, icon: '⚙️', label: 'Process', description: 'Detect and process faces in photos' }, - { id: 'identify' as PageId, icon: '👤', label: 'Identify People', description: 'Manually identify people in faces' }, - { id: 'auto-match' as PageId, icon: '🤖', label: 'Auto-Match', description: 'Automatically match similar faces to previously identified faces' }, - { id: 'search' as PageId, icon: '🔍', label: 'Search Photos', description: 'Search and filter photos' }, - { id: 'modify' as PageId, icon: '✏️', label: 'Modify People', description: 'Edit person information' }, - { id: 'tags' as PageId, icon: '🏷️', label: 'Tag Photos', description: 'Tag photos and manage photo tags' }, + const mainNavItems: HelpNavItem[] = [ + { id: 'scan', icon: FolderInput, label: 'Scan', description: 'Import photos from local folders or network paths' }, + { id: 'process', icon: Settings, label: 'Process', description: 'Detect faces in scanned photos' }, + { id: 'identify', icon: User, label: 'Identify', description: 'Name unknown faces in photos and videos' }, + { id: 'auto-match', icon: Bot, label: 'Auto-Match', description: 'Review suggested matches to known people' }, + { id: 'search', icon: Search, label: 'Search photos', description: 'Browse by person, date, tags, or folder' }, + { id: 'modify', icon: Pencil, label: 'Modify', description: 'Edit person details and fix identifications' }, + { id: 'tags', icon: Tags, label: 'Tag photos', description: 'Organize photos with tags' }, ] - const maintenanceNavItems = [ - { id: 'faces-maintenance' as PageId, icon: '🔧', label: 'Faces', description: 'Remove unwanted faces from the database (under Maintenance tab)' }, - { id: 'user-identified-faces' as PageId, icon: '✅', label: 'User Identified Faces', description: 'Approve or deny face identifications made by users (under Maintenance tab)' }, - { id: 'user-reported-photos' as PageId, icon: '🚨', label: 'User Reported Photos', description: 'Review and manage photos reported by users (under Maintenance tab)' }, - { id: 'user-tagged-photos' as PageId, icon: '🏷️', label: 'User Tagged Photos', description: 'Approve or deny tags suggested by users (under Maintenance tab)' }, - { id: 'user-uploaded-photos' as PageId, icon: '📤', label: 'User Uploaded Photos', description: 'Approve or reject photos uploaded by users (under Maintenance tab)' }, - { id: 'users' as PageId, icon: '👥', label: 'Users', description: 'Manage backend users, frontend users, and roles (under Maintenance tab)' }, + const maintenanceNavItems: HelpNavItem[] = [ + { id: 'faces-maintenance', icon: Wrench, label: 'Faces', description: 'Remove low-quality or false-positive face detections' }, + { id: 'user-identified-faces', icon: UserCheck, label: 'User-identified faces', description: 'Approve or deny identifications submitted by viewer users' }, + { id: 'user-reported-photos', icon: Flag, label: 'Reported photos', description: 'Review photos flagged by viewer users' }, + { id: 'user-tagged-photos', icon: Tag, label: 'User-tagged photos', description: 'Approve or deny tag suggestions from viewer users' }, + { id: 'user-uploaded-photos', icon: Upload, label: 'User uploads', description: 'Approve or reject uploads from viewer users' }, + { id: 'users', icon: Users, label: 'Users', description: 'Manage admin accounts, viewer accounts, and roles' }, ] return (
-

Navigation Overview

+

Navigation overview

- The application uses a left sidebar navigation with the following pages. Click on any page to learn more about it: + Use the left sidebar to move between pages. Pick a page below for a short walkthrough.

- +
-

Main Pages

+

Main pages

{mainNavItems.map((item) => ( - + ))}
-

Maintenance Tab

-

These pages are located under the Maintenance tab in the navigation sidebar:

+

Maintenance

+

+ These pages live under the Maintenance section in the sidebar. +

{maintenanceNavItems.map((item) => ( - + ))}
@@ -144,15 +163,17 @@ function NavigationOverview({ onPageClick }: { onPageClick: (page: PageId) => vo ) } -function PageHelpLayout({ title, onBack, children }: { title: string; onBack: () => void; children: React.ReactNode }) { +function PageHelpLayout({ title, onBack, children }: { title: string; onBack: () => void; children: ReactNode }) { return (

{title}

@@ -352,10 +373,10 @@ function IdentifyPageHelp({ onBack }: { onBack: () => void }) {
  • View the current face on the left panel
  • To exclude a face from identification:
      -
    • Click the 🚫 button next to the face counter
    • +
    • Click the Exclude button next to the face counter
    • The face will be marked as excluded and automatically skipped
    • Excluded faces are hidden by default (check "Include excluded faces" in filters to see them)
    • -
    • Click the 🚫 button again to include the face back in identification
    • +
    • Click Exclude again to include the face back in identification
  • Choose how to identify: @@ -379,11 +400,11 @@ function IdentifyPageHelp({ onBack }: { onBack: () => void }) {

    Confidence Colors (Similar Faces):

      -
    • 🟢 80%+ = Very High (Almost Certain)
    • -
    • 🟡 70%+ = High (Likely Match)
    • -
    • 🟠 60%+ = Medium (Possible Match)
    • -
    • 🔴 50%+ = Low (Questionable)
    • -
    • <50% = Very Low (Unlikely)
    • +
    • 80%+ — very high (almost certain)
    • +
    • 70%+ — high (likely match)
    • +
    • 60%+ — medium (possible match)
    • +
    • 50%+ — low (questionable)
    • +
    • <50% — very low (unlikely)
  • @@ -435,7 +456,7 @@ function IdentifyPageHelp({ onBack }: { onBack: () => void }) {
  • Tag filtering lets you identify faces only from photos with specific tags
  • Click on face images to open the full photo in a new window
  • For videos, you can identify multiple people in the same video
  • -
  • Use the exclude (🚫) button to exclude unknown, unwanted faces, or low-quality faces from appearing in your identification workflow
  • +
  • Use the Exclude button to skip unknown, unwanted, or low-quality faces in your identification workflow
  • Excluded faces are hidden by default, but you can view them by checking "Include excluded faces" in the filters
  • @@ -471,7 +492,7 @@ function AutoMatchPageHelp({ onBack }: { onBack: () => void }) {

    Automatic Matching:

    1. Navigate to Auto-Match page
    2. -
    3. Click "🚀 Run Auto-Match" button
    4. +
    5. Click Run Auto-Match
    6. The system will automatically match unidentified faces to identified people based on:
      • Similarity higher than 85%
      • @@ -499,9 +520,9 @@ function AutoMatchPageHelp({ onBack }: { onBack: () => void }) {
      • Check boxes next to faces you want to identify to this person
    7. -
    8. Use "☑️ Select All" or "☐ Clear All" to quickly select/deselect all matches
    9. -
    10. Click "💾 Save changes for [Person Name]" button to save your selections
    11. -
    12. Use "⏮️ Back" and "⏭️ Next" buttons (or Prev/Next in the person panel) to navigate between people
    13. +
    14. Use Select All or Clear All to quickly select or deselect matches
    15. +
    16. Click Save changes for [Person Name] to save your selections
    17. +
    18. Use Back and Next (or Prev/Next in the person panel) to move between people
    19. Use the search box to find specific people by last name
    @@ -597,8 +618,8 @@ function SearchPageHelp({ onBack }: { onBack: () => void }) {
  • Use "Select all" to select all photos in current results
  • Use "Unselect All" to clear selection
  • Click on a photo path to open the photo in a new window
  • -
  • Click the folder icon (📁) to open the photo's folder in your file manager
  • -
  • Click the star icon (⭐) to toggle favorite status for individual photos
  • +
  • Click the folder icon to open the photo folder in your file manager
  • +
  • Click the star icon to toggle favorite status for individual photos
  • Click column headers to sort results
  • @@ -610,9 +631,9 @@ function SearchPageHelp({ onBack }: { onBack: () => void }) {
  • Choose an action:
  • @@ -621,7 +642,7 @@ function SearchPageHelp({ onBack }: { onBack: () => void }) {

    Using the Photo Viewer:

      -
    1. Click "▶ Play photos" button to open the full-screen viewer
    2. +
    3. Click Play photos to open the full-screen viewer
    4. If you have photos selected, the viewer will start with the first selected photo
    5. Use arrow keys or on-screen buttons to navigate between photos
    6. Press Escape or click the X button to close the viewer
    7. @@ -683,7 +704,7 @@ function ModifyPageHelp({ onBack }: { onBack: () => void }) {

      Editing Person Information:

      1. Select a person from the list
      2. -
      3. Click the edit icon (✏️) next to their name
      4. +
      5. Click the edit icon next to their name
      6. In the dialog, update any fields:
        • First Name (required)
        • @@ -735,7 +756,7 @@ function ModifyPageHelp({ onBack }: { onBack: () => void }) {

          Deleting a Person:

          1. Select a person from the list
          2. -
          3. Click the delete icon (🗑️) next to their name
          4. +
          5. Click the delete icon next to their name
          6. Read the warning message carefully - this will:
            • Unlink all faces from this person
            • @@ -745,7 +766,7 @@ function ModifyPageHelp({ onBack }: { onBack: () => void }) {
          7. Click "Delete" to confirm, or "Cancel" to abort
          8. -
          9. ⚠️ Warning: This action cannot be undone!
          10. +
          11. Warning: This action cannot be undone.
    @@ -814,7 +835,7 @@ function TagsPageHelp({ onBack }: { onBack: () => void }) {
    1. Tag Individual Photo:
    @@ -1115,7 +1136,7 @@ function UserReportedPhotosPageHelp({ onBack }: { onBack: () => void }) {
    -

    ⚠️ Important Warnings

    +

    Important warnings

    @@ -1355,7 +1376,7 @@ function UsersPageHelp({ onBack }: { onBack: () => void }) {

    Features