Skip to content

feat: Comprehensive Tool Management System#1

Merged
ril3y merged 18 commits intomainfrom
tool-support
Oct 12, 2025
Merged

feat: Comprehensive Tool Management System#1
ril3y merged 18 commits intomainfrom
tool-support

Conversation

@ril3y
Copy link
Copy Markdown
Owner

@ril3y ril3y commented Oct 12, 2025

Summary

Implements a complete tool management system with checkout/checkin functionality, maintenance tracking, and enhanced UI with proper theme integration.

Changes

Backend

  • ✅ Add is_checkable field to tool model for checkout control
  • ✅ Implement checkout/return endpoints with user tracking
  • ✅ Add maintenance record management (CRUD operations)
  • ✅ Enhance tool search with multiple filters
  • ✅ Add tool statistics endpoint
  • ✅ Update admin setup with tool permissions
  • ✅ Fix maintenance record schema (make performed_by optional)

Frontend

  • ✅ Implement full-featured ToolModal for create/edit
  • ✅ Add ToolDetailModal with maintenance records
  • ✅ Implement checkout/checkin UI flows
  • ✅ Add tool statistics dashboard
  • ✅ Fix checkin endpoint URL (/return instead of /checkin)
  • ✅ Add maintenance record management UI
  • ✅ Apply proper theme colors throughout ToolDetailModal
  • ✅ Fix Lucide-react icon imports

Database

  • ✅ Add is_checkable column to tools (default TRUE)
  • ✅ Add checkout tracking fields
  • ✅ Migrate existing tools to support new schema

Test Plan

  • Create new tool
  • Edit existing tool
  • Checkout tool (when is_checkable)
  • Check-in tool
  • Add maintenance record
  • Delete maintenance record
  • Verify theme colors in light/dark mode
  • Run CI/CD pipeline (automated)

Screenshots

  • Tools page with checkout status
  • Tool detail modal with theme colors
  • Maintenance record management

🤖 Generated with Claude Code

ril3y and others added 18 commits October 11, 2025 10:30
ROOT CAUSE:
The conditional logic was correct but the variable was being recalculated
inside the handler function instead of using the component-level variable.

FIXES:
- Use single source of truth: requireCurrentPassword = !isAdmin || isEditingSelf
- Admin editing another user: !true || false = FALSE → hide field ✅
- Admin editing self: !true || true = TRUE → show field ✅
- Regular user: !false || ? = TRUE → show field ✅
- Update UI conditional to use requireCurrentPassword directly
- Add comprehensive debug logging to console

DEBUG ADDED:
Console logs now show:
- Current User ID
- Editing User ID
- User roles
- isAdmin flag
- isEditingSelf flag
- requireCurrentPassword result

This makes it crystal clear what the modal is detecting.

DO NOT PUSH - TESTING LOCALLY FIRST
Changed all password input fields from 'input-field' to 'input' class
to fix white-on-white text visibility issue.

Fixed fields:
- Current Password input
- New Password input
- Confirm New Password input

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Added missing validation checks for password changes:
- Uppercase letter requirement
- Lowercase letter requirement
- Number requirement
- Updated hint text to match CreateUserModal

This ensures password changes have same validation as user creation,
preventing backend 422 errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Removed the default credentials text (admin / Admin123!) from the
login page UI for security reasons. Users should receive credentials
through secure channels or use the emergency password reset script
if needed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ownloads

This commit implements a comprehensive solution for datasheet handling across
all suppliers (DigiKey, Mouser, LCSC) with standardized storage, async downloads,
and proper PDF extraction.

Backend Changes:
- Standardize datasheet storage to `additional_properties.datasheet_url` across all suppliers
- Update supplier_data_mapper.py to ensure consistent datasheet URL mapping with fallback logic
- Implement background task system for datasheet downloads (TaskType.DATASHEET_DOWNLOAD)
- Add datasheet_download_task.py handler for async downloads with progress updates
- Fix part_enrichment_service.py to create download tasks instead of blocking downloads
- Fix LCSC supplier to extract actual PDF URLs from HTML wrapper pages by fetching
  the datasheet page and parsing iframe src attributes

Frontend Changes:
- Fix PartDetailsPage.tsx datasheet URL paths from /api/utility/static/ to /static/
- Fix api.ts getPDFProxyUrl() to use correct /api/utility/static/proxy-pdf endpoint
- Update PDFViewer.tsx to handle /static/ URLs for authentication

LCSC Datasheet Extraction:
- LCSC returns HTML wrapper pages at URLs like https://www.lcsc.com/datasheet/C427380.pdf
- The actual PDF is embedded in an iframe (e.g., https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/C427380.pdf)
- Implemented async _parse_product_page_html() to fetch datasheet pages separately
- Extract iframe src with regex pattern to get direct PDF URLs
- Supports multiple extraction methods: pdfUrl from JSON-LD, iframe src, and previewPdfUrl

Testing:
- Verified LCSC part C427380 correctly extracts direct PDF URL
- Confirmed datasheet downloads work as background tasks
- Frontend PDF viewer displays downloaded datasheets correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…heet download logging

This commit fixes two enrichment-related issues:

1. **Mouser Enrichment Requirements**:
   - Added `get_enrichment_requirements()` method to Mouser supplier
   - Defines required field: supplier_part_number (Mouser Part Number)
   - Defines recommended fields: manufacturer_part_number, manufacturer
   - Defines optional fields: description, component_type
   - Enrichment modal now properly shows which fields are missing for Mouser parts
   - Matches DigiKey's enrichment requirements pattern

2. **Datasheet Download Task Logging**:
   - Enhanced logging in part_enrichment_service.py to debug datasheet downloads
   - Now checks both `standardized_data.additional_properties.datasheet_url` and
     `part.additional_properties.datasheet_url` for the datasheet URL
   - Added INFO-level logs to track:
     - When datasheet URL is found
     - When datasheet download task is created
     - When datasheet is already downloaded (skip case)
   - Helps diagnose why datasheets might not be downloading during enrichment

Testing:
- Mouser enrichment modal now displays required fields correctly
- Datasheet download task creation is now more visible in logs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fixed an issue where supplier part numbers entered in the enrichment modal
were being lost after enrichment completed. The problem affected both
Mouser and DigiKey enrichment workflows.

Changes:
- Updated part_enrichment_service to save supplier_part_number to database
- Modified supplier data mappers to preserve user-entered part numbers
- Added flag_modified for supplier_part_number in SQLAlchemy updates
- Prevented mappers from overwriting existing supplier part numbers

The fix ensures that when users enter a supplier part number (e.g.,
Mouser: 652-CRL0805JWR330ELF) in the enrichment modal, it persists
after enrichment so they don't need to re-enter it for future enrichments.

Tests added to verify the fix works correctly for both Mouser and DigiKey.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- DigiKey and Mouser Excel files were being misidentified or rejected
- Detection logic had overly permissive fallbacks that accepted any file
- Mouser files with "Mouser #:" column header were not recognized
- CSV detection used only generic indicators causing false matches

**Root Causes:**
1. DigiKey's can_import_file() had three permissive fallbacks:
   - Excel read exception returned True (accepted unreadable files)
   - CSV detection used only generic indicators without DigiKey-specific checks
   - Outer exception handler returned True (blindly accepted files)

2. Mouser's can_import_file() had same permissive fallback issues
3. Mouser-specific indicators list missing 'mouser #' pattern

**Changes:**

DigiKey (digikey.py):
- Changed Excel exception handler to return False instead of True (line 1316)
- Implemented two-tier CSV detection requiring BOTH:
  - DigiKey-specific indicators: 'Digi-Key Part Number', 'DigiKey Part', 'Customer Reference', 'dk_products'
  - Generic indicators: 'Manufacturer Part Number', 'Part Number', 'Quantity'
- Changed outer exception handler to return False (line 1337)

Mouser (mouser.py):
- Added 'mouser #' to mouser_specific_indicators list (line 875)
- Implemented two-tier Excel detection requiring BOTH:
  - Mouser-specific indicators: 'mouser part', 'mouser p/n', 'mouse part', 'mouser no', 'mouser #'
  - Generic indicators: 'manufacturer part number', 'part number', 'quantity', 'unit price'
- Changed exception handlers to return False instead of True
- Added debug logging to track detection logic

**Testing:**
- DigiKey CSV files: ✅ Working
- Mouser Excel files with "Mouser #:" header: ✅ Should now work
- Files are properly rejected when they don't match supplier patterns

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fixed 'NoneType' object has no attribute 'lower' error in part_service.py
when creating parts without a supplier field.

**Changes:**
- Updated supplier field handling to safely handle None values
- Changed from `part_data.get("supplier", "").lower()` to
  `(part_data.get("supplier") or "").lower()`

**Task 1 Investigation Results:**
- Location rename does NOT break allocations (database schema is correct)
- Foreign keys use UUID id fields, not names
- test_database_integrity_after_rename now passes
- This bug was blocking the validation tests

**Technical Details:**
- Location ID uses UUID, not name, so renames preserve all references
- PartLocationAllocation foreign key: location_id → locationmodel.id
- All allocation references remain intact through location renames

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fixed bug where updating a location (e.g., changing name) would clear
the image_url and emoji fields.

**Root Cause:**
- Used `model_dump(exclude_unset=False)` which included all fields with None
- Logic incorrectly included image_url/emoji even when None, overwriting existing values

**Solution:**
- Changed to `model_dump(exclude_unset=True)` to only include explicitly set fields
- Simplified update logic by removing special-case handling

**Changes:**
- `locations_routes.py`: Simplified update_location endpoint (lines 138-141)
- Added comprehensive test suite: `test_location_image_persistence.py`

**Tests Added:**
✓ test_location_update_preserves_image - Verifies image persists through name updates
✓ test_location_update_can_clear_image - Verifies explicit None clears image
✓ test_location_update_preserves_emoji - Verifies emoji persists through updates
✓ test_database_level_image_persistence - Verifies DB-level integrity

All 4 tests passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
## Problem
When creating a part with an emoji, the emoji was being saved to the
database correctly (it was already in valid_part_fields), but the API
response was not including the emoji field. This caused the frontend
to show emoji as None even though it was successfully stored.

## Root Cause
In part_service.py add_part() method, the safe_part_dict that is
returned after creating a part did not include the emoji field. The
response was manually constructed and only included:
- id, part_name, part_number, description
- quantity, supplier, image_url
- categories, location

But missing: emoji

## Solution
Added emoji field to the safe_part_dict response in add_part():
```python
safe_part_dict = {
    # ... existing fields ...
    "emoji": part_obj.emoji,  # Added this line
    # ... rest of fields ...
}
```

## Testing
Created comprehensive test suite: tests/test_part_emoji_persistence.py
- test_part_creation_saves_emoji: Verifies emoji saves on creation
- test_part_update_preserves_emoji: Verifies emoji persists through updates
- test_part_emoji_can_be_changed: Verifies emoji can be changed
- test_part_emoji_can_be_cleared: Verifies emoji can be set to None
- test_part_creation_without_emoji: Verifies parts work without emoji
- test_database_level_emoji_persistence: Verifies DB-level persistence

All 6 tests passing ✓

## Files Changed
- MakerMatrix/services/data/part_service.py: Added emoji to response
- tests/test_part_emoji_persistence.py: New comprehensive test suite

## Impact
Task 3 from tasks.md (MEDIUM priority) is now complete:
- ✓ Emoji saves correctly on part creation
- ✓ Backend properly handles emoji in create operation
- ✓ Frontend receives emoji data in create response
- ✓ Tests verify emoji persistence on create
- ✓ All GitHub Actions should pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ive matching

- Normalize CSV content for case-insensitive and punctuation-insensitive matching
- Remove colons, hyphens, and underscores before matching header patterns
- Add additional DigiKey-specific patterns (e.g., 'digi key p/n')
- Add generic patterns for better detection coverage (e.g., 'mfr part')
- Fixes issue where DigiKey CSV files were rejected despite being supported

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Adds complete tool management functionality as an alternative to parts tracking:

**Core Models**
- ToolModel: Main tool model with maintenance, checkout, and condition tracking
- ToolLocationAllocation: Location-based allocation system (same as parts)
- ToolCategoryLink: Many-to-many relationship with categories

**Key Features**
- Single-item or small-quantity tracking (vs bulk part inventory)
- Tool checkout/return system with user tracking
- Maintenance scheduling and history
- Condition tracking (excellent, good, fair, poor, needs_repair, out_of_service)
- Tool-specific properties (size, voltage, specifications, etc.)
- Purchase tracking and valuation
- Calibration and consumable flags
- Excluded from part analytics by default

**Service Layer**
- ToolService with full CRUD operations
- Checkout/return functionality
- Maintenance record management
- Advanced search with multiple filters
- Statistics and reporting

**API Endpoints**
- POST /api/tools - Create tool
- GET /api/tools/{tool_id} - Get tool by ID
- GET /api/tools - List all tools (paginated)
- PUT /api/tools/{tool_id} - Update tool
- DELETE /api/tools/{tool_id} - Delete tool
- POST /api/tools/search - Advanced search
- POST /api/tools/{tool_id}/checkout - Checkout tool
- POST /api/tools/{tool_id}/return - Return tool
- POST /api/tools/{tool_id}/maintenance - Record maintenance
- GET /api/tools/statistics - Get tool statistics

**Integration**
- Tools share category and location infrastructure with parts
- Categories support both parts and tools
- Locations support both part and tool allocations
- Tools can coexist with parts in same containers

**Database Schema**
- toolmodel table with all tool fields
- tool_location_allocations for multi-location support
- tool_category_links for category relationships
- Proper foreign keys and cascade deletes

Acceptance criteria met:
✅ Tool database model created
✅ Tools support categories and locations
✅ Tools can coexist with parts in same containers
✅ No "low stock" analytics for tools (exclude_from_analytics flag)
✅ Backend API endpoints created
✅ Follows same patterns as PartService/part_routes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Create ToolsPage component with list/grid view and search/filter/sort
- Create ToolModal for creating and editing tools
- Create ToolDetailModal for viewing tool details with checkout/checkin
- Implement tools.service.ts API client
- Add tool types and interfaces
- Add Tools menu item to navigation
- Add routing for /tools page
- Create basic frontend tests for tool components
- Fix category_models.py to properly import ToolCategoryLink
- Successfully test tool creation via API

All acceptance criteria met:
✅ Tool listing page created
✅ Tool creation/edit modals
✅ Tool detail view
✅ Location assignment UI
✅ Category management integration
✅ Frontend tests complete
✅ Backend API integration working
Replace non-existent CircleCheck/CircleX with CheckCircle/XCircle.
Fixes import error preventing tools page from loading.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Frontend fixes:
- Fix tool creation validation errors (422) by matching backend schema
- Update Tool interface to match backend ToolResponse exactly
- Fix field name mismatches (name→tool_name, model→model_number)
- Convert empty strings to undefined for optional datetime fields
- Fix tools.service to unwrap ApiResponse structure correctly
- Update ToolsPage to use correct field references and status handling
- Fix ToolDetailModal to use PartImage component and correct fields
- Improve search/filter layout - search bar full width, filters on separate row

Backend fixes:
- Change image upload from center crop to letterboxing/padding
- Use max(width, height) instead of min(width, height) for square size
- Add white borders to preserve entire image instead of cropping
- Update docstrings to reflect letterboxing behavior

This fixes:
1. Tool creation 422 validation errors
2. Tools not displaying after creation
3. Tool detail modal crashes
4. Image cropping that cut off top/bottom of tall images
5. Search bar layout pushing "Add Tool" button off screen

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
… record schema

- Update ToolDetailModal to use proper theme colors (text-theme-primary, bg-theme-secondary, etc.)
- Fix maintenance record 422 error by making performed_by optional in schema (backend sets it automatically)
- Replace invalid Tool icon import with Settings icon for maintenance section
- All text now properly visible with theme-aware colors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Complete implementation of tool management with checkout/checkin functionality, maintenance tracking, and enhanced UI.

Backend Changes:
- Add is_checkable field to tool model for checkout control
- Implement checkout/return endpoints with user tracking
- Add maintenance record management (CRUD operations)
- Enhance tool search with multiple filters
- Add tool statistics endpoint
- Update admin setup with tool permissions

Frontend Changes:
- Implement full-featured ToolModal for create/edit
- Add ToolDetailModal with maintenance records
- Implement checkout/checkin UI flows
- Add tool statistics dashboard
- Fix checkin endpoint URL (/return instead of /checkin)
- Add maintenance record management UI

Database:
- Add is_checkable column to tools (default TRUE)
- Add checkout tracking fields
- Migrate existing tools to support new schema

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ril3y ril3y merged commit 6b14e0e into main Oct 12, 2025
1 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant