Extended createDocumentPreview to handle 'image' type, displaying an appropriate icon. Added logic in setupExistingFilePreview to use the image icon for Paperless image files, preventing broken image links. This improves the user experience when previewing image documents from Paperless integrations.
- Add /preview endpoints for Paperless and Papra integrations
- Proxy external images with authentication to fix broken image previews
- Include preview URLs in search results and attachment data
- Update modal and asset renderers to use preview URLs for images
- Add 1-hour browser caching for improved performance
- Maintain fallback to document icons for non-images
- Fix missing image icon issue in asset and sub-asset modals
Resolves broken image previews for external photos from document management integrations.
- Add complete Home Assistant integration with schema-driven configuration
- Support for importing HA entities (devices/sensors) as assets
- Intelligent categorization based on entity domains (light, sensor, switch, etc.)
- Device filtering and bulk import functionality
- Custom SVG logo and branded CSS styles
- API endpoints for connection testing, device listing, and import
- Automatic conversion of HA entities to DumbAssets format with custom fields
- Registered in integration manager following established patterns
- Add Paperless NGX icon badges to identify documents from Paperless integration
- Implement badge detection using isPaperlessDocument flag in file metadata
- Update setupExistingFilePreview to accept and process file info parameters
- Modify createPhotoPreview and createDocumentPreview to support badge rendering
- Pass file metadata through all modal manager setupExistingFilePreview calls
- Position badges in top-left corner (14px top, 8px left) of file preview items
- Style badges as clean 20x20px icons with drop-shadow for contrast
- Ensure badge persistence when reopening edit modals for existing assets
- Support badges in both asset and sub-asset file previews across all types
- Maintain consistent badge appearance in modal and asset detail views
Paperless documents are now clearly identified with the Paperless NGX logo
badge, providing immediate visual recognition of document source across
all file preview contexts in the application.
Files modified:
- public/styles.css: Badge styling and positioning
- src/services/render/previewRenderer.js: Badge rendering logic
- public/managers/modalManager.js: File info parameter passing
fix(security): prevent API token exposure to frontend in settings endpoint
CRITICAL SECURITY FIX: Paperless NGX API tokens were being exposed to the
frontend through the /api/settings endpoint, creating a serious security
vulnerability.
Changes:
- Backend: Sanitize GET /api/settings to replace real API tokens with placeholder
- Backend: Enhanced test connection endpoint to handle both saved and new tokens
- Backend: Smart settings save preserves existing tokens when placeholder received
- Frontend: Show placeholder for saved tokens instead of exposing real values
- Frontend: Improved UX with dynamic placeholders and token field handlers
- Integration: Updated isEnabled() method to recognize placeholder tokens
Security Impact:
- BEFORE: Real API tokens sent to frontend (HIGH RISK)
- AFTER: Only placeholder (*********************) sent to frontend (SECURE)
Features:
- Users can test connections without re-entering saved tokens
- Clear feedback when tokens are saved vs need to be entered
- Backward compatible with existing configurations
- Maintains full Paperless integration functionality
Files modified:
- server.js: API endpoints sanitization and token handling
- public/managers/settings.js: Frontend token management and UX
- src/integrations/paperless.js: Integration compatibility fix
* feat: implement asset and component duplication feature (#79)
Add comprehensive duplication functionality allowing users to create multiple
copies of existing assets and components with sequential naming and sanitized data.
- **Duplicate Buttons**: Added duplicate buttons between Save/Cancel in asset and sub-asset modals
- **Smart Visibility**: Buttons only appear in edit mode (not when creating new items)
- **Sequential Naming**: Duplicates automatically named with incremental numbers (e.g., "Asset Name (1)", "Asset Name (2)")
- **Input Validation**: Users can create 1-100 duplicates with proper validation
- **Data Sanitization**: Excludes serial numbers, warranty info, files, and maintenance events from duplicates
- **Bulk Operations**: Efficient server-side bulk creation endpoints
- **public/index.html**:
- Added duplicate buttons to asset and sub-asset modal form-actions
- Added duplicate confirmation modal with count input
- Updated help text to explain sequential naming and data exclusions
- **public/styles.css**:
- Added styling for duplicate buttons with hover states
- Added duplicate modal and input styling
- Added responsive design support
- **public/managers/modalManager.js**:
- Extended with complete duplication functionality
- Added duplicate modal management methods
- Implemented sequential naming logic
- Added data sanitization for duplicates
- **public/script.js**:
- Added duplicate modal to escape key handler
- Added click-off-to-close functionality
- Exposed refreshAllData for ModalManager access
- **server.js**:
- Added `/api/assets/bulk` endpoint for bulk asset creation
- Added `/api/subassets/bulk` endpoint for bulk sub-asset creation
- Added validation for bulk operations (max 100 items)
- Added notification support for bulk operations
- Restored accidentally removed endpoints (settings, uploads, etc.)
- **Data Processing**: Strips sensitive data while preserving core asset information
- **Error Handling**: Comprehensive validation and user feedback
- **Performance**: Bulk server operations for efficiency
- **UX**: Loading states, success messages, keyboard shortcuts
Perfect for scenarios like:
- Creating multiple identical hard drives with different serial numbers
- Duplicating network equipment across locations
- Bulk adding similar components to inventory
- Setting up template assets for mass deployment
Resolves#79
* reorg styles.css
* Add properties grid to duplicate asset/subasset modal
* Add components and sub components duplication option to duplicate grid
* update file previews for subcomponents to support multiple file paths
* minor updates to duplicate modal styling
* update duplicate sub asset button icon
* Add duplicate button to asset / subasset actions
* apply svg styles to duplicate asset-actions buttons
* Fix component's sub component duplication
* Fix rendering proper asset / subasset after duplication
* refactor duplication/clone assets code into it's own manager class as well as closing asset modals after performing duplication
* fix asset navigation after duplicating an asset
---------
Co-authored-by: gitmotion <43588713+gitmotion@users.noreply.github.com>
* Added Quantity Field
✅ Completed Changes
1. HTML Forms
Added quantity input fields to both asset and sub-asset forms
Fields default to value="1" with min="1" validation
Positioned after price fields for logical flow
2. Modal Manager (Frontend)
Updated form data collection to capture quantity values
Added quantity to form population for editing existing assets
Ensures quantity defaults to 1 if not provided
3. Asset Details Display
Added quantity field to asset info display in assetRenderer.js
Shows "Quantity: X" in the asset details section
Includes fallback to 1 for backwards compatibility
4. Server-side Backwards Compatibility
Asset Creation: Ensures quantity defaults to 1 if not provided
Asset Updates: Preserves existing quantity or defaults to 1
Sub-asset Creation: Ensures quantity defaults to 1 if not provided
Sub-asset Updates: Preserves existing quantity or defaults to 1
Data Loading: All assets and sub-assets get quantity field when loaded from API
5. Import Functionality
Added quantity column mapping option in import modal
Updated server-side import logic to handle quantity with default of 1
Updated template CSV download to include quantity example
Added auto-mapping for "quantity" and "qty" column headers
🔒 Backwards Compatibility
The implementation ensures that:
Existing assets without quantity will automatically show quantity: 1
All API endpoints handle missing quantity gracefully
Import functionality works with or without quantity columns
No data migration is required - compatibility is handled at runtime
* Add "Total Value" to asset details
✅ Total Value Field Features
Conditional Display
Only shows when quantity > 1
Only shows when there's a valid price (either price or purchasePrice)
Calculation
Calculates: price × quantity = total value
Uses the same currency formatting as other price fields
Handles both asset.price and asset.purchasePrice (for backwards compatibility)
* Updated Dashboard Value Calculation logic
Updated Calculation Logic to account for quantity
Assets:
price × quantity for each asset
Defaults to quantity = 1 for backwards compatibility
Sub-Assets:
purchasePrice × quantity for each sub-asset
Defaults to quantity = 1 for backwards compatibility
* Added Multi-file uploads
Allows users to upload multiple files for each file type.
Reworked cascading deletion logic to ensure Deleting an asset deletes all attachments as well as sub and sub sub assets and applicable attachments.
* Added file names as file-label
change file label to be file name, limited to 15 characters then cuts off.
* Fix removed features
Fixed Issues:
1. Asset Update Endpoint (PUT /api/assets/:id)
✅ Added name validation: Now validates that updatedAssetData.name exists before proceeding
✅ Added edit notification logic: Checks notificationSettings.notifyEdit and sends notifications when assets are edited
✅ Added debug logging: Logs asset update events when DEBUG mode is enabled
2. Sub-asset Update Endpoint (PUT /api/subassets/:id)
✅ Added name validation: Now validates that updatedSubAssetData.name exists before proceeding
✅ Added edit notification logic: Checks notificationSettings.notifyEdit and sends notifications when sub-assets are edited
✅ Added debug logging: Logs sub-asset update events when DEBUG mode is enabled
3. Asset Delete Endpoint (DELETE /api/asset/:id)
✅ Added delete notification logic: Checks notificationSettings.notifyDelete and sends notifications when assets are deleted
✅ Added debug logging: Logs notification attempts when DEBUG mode is enabled
4. Sub-asset Delete Endpoint (DELETE /api/subasset/:id)
✅ Added delete notification logic: Checks notificationSettings.notifyDelete and sends notifications when sub-assets are deleted
✅ Added debug logging: Logs notification attempts when DEBUG mode is enabled
Key Features Restored:
Data Integrity: Name field validation prevents saving assets/sub-assets without names
Notification System: Complete notification support for edit and delete operations
Consistent API: All endpoints now follow the same pattern as the create endpoints
Error Handling: Proper error handling for notification failures (won't break the main operation)
Debug Support: Comprehensive logging for troubleshooting when DEBUG=TRUE
* Reworked file label and preview grid formatting
* Fix file duplication on drag and drop
There are two separate drag and drop implementations that both trigger when files are dropped:
First implementation: In setupFileInputPreview() (lines ~105-140) - this sets up drag and drop for each individual upload box
Second implementation: In setupDragAndDrop() (lines ~300-389) - this also sets up drag and drop for all upload boxes
Both functions are being called, and they're both adding event listeners to the same elements, causing files to be processed twice. The fix is to remove the duplicate drag and drop code from setupFileInputPreview() since setupDragAndDrop() handles it more comprehensively.
* Fix file duplication on second drag & drop
The Solution:
Added file tracking: Each setupFileInputPreview() function now maintains a processedFiles Set that tracks which files have already been processed using a unique identifier.
Unique file identification: Each file is identified by ${file.name}-${file.size}-${file.lastModified}, which creates a unique fingerprint for each file.
Skip already processed files: Before creating a preview, the handler checks if the file has already been processed and skips it if so.
Clean up on deletion: When a file is deleted, its ID is removed from the processedFiles Set so it can be re-added later if needed.
* feat: completely rework file upload system to prevent duplicates and fix state issues
- Add comprehensive file state tracking with allFiles, newFilesSet, and filePreviewMap
- Implement additive file selection behavior for both drag-and-drop and file input
- Add deduplication logic to prevent duplicate files across multiple selections
- Create distinction between new files (for upload) vs existing files (preview only)
- Add reset() functionality to prevent state contamination between modal operations
- Fix existing file preview display by using server paths instead of mock File objects
- Fix individual file deletion by integrating with filesToDelete system using deletion markers
- Update handleFileUploads to only upload truly new files, preventing re-upload of existing files
- Add setupExistingFilePreview() for proper existing file integration with new helpers
- Update modal manager to use new helper functions and proper state clearing
- Add robust error handling and backward compatibility fallbacks
Fixes:
- File duplication when editing existing assets
- Missing image previews for existing files in edit modal
- Broken individual file deletions
- State persistence contamination between different asset operations
- Inconsistent behavior between drag-and-drop and file input selection
The file upload system now properly handles all edge cases while maintaining
a clean, predictable state across different operations.
* Add logic for import file drag and drop and restricting to single file
remove single attribute
adding self reference to eventlisteners for drag and drop
simplifying resetfileupload per file input
---------
Co-authored-by: gitmotion <43588713+gitmotion@users.noreply.github.com>
* Add hyperlink icon to asset details
Add hyperlink icon in both asset and sub asset details allowing users to copy a direct link to icons for barcode/QR code compatibility.
* Fix render timing to fix query param
I've resolved the query parameter timing issue by:
1. Fixed Initialization Order
Moved dashboardManager initialization before handleUrlParameters() is called
Removed duplicate dashboard manager initialization
Removed premature dashboard rendering in loadAllData()
2. Ensured Proper Flow
Data loads first (loadAllData())
Dashboard manager gets initialized with all dependencies
URL parameters are processed with all managers ready
Only if no URL parameters are found does the dashboard render
* Update README.md
Added maintenance feature
Added tagging feature
Added dependencies
* Include Global filters for Event List
Enhanced updateEventsDisplay() method - Now respects both local events filters (All, Warranty, Maintenance) AND global dashboard filters (Components, Warranties, Expired, Within 30 days, Within 60 days, Active)
Updated Dashboard Card Click Handlers - Now call updateEventsDisplay() when dashboard filters are applied
Enhanced initializeEventsSection() - Ensures events are properly filtered from the start
Added refreshEventsDisplay() method - Public method for external refreshing of events
* Updated/Fixed "Active" filtering logic
The "Active" filter had an incorrect third condition that was including assets with ANY component warranties, regardless of whether those warranties were actually active or expired.
* Fixed EventList updating
The issue was that sectionVisibility was a local variable in the renderDashboard method, so it wasn't accessible in the click handler scope when the dashboard cards were clicked later.
* add logging for event list debug
* Fix Event List updating
The Problem
The Events list wasn't updating when global dashboard filters were clicked because of a variable scope issue:
Two separate dashboardFilter variables existed:
One in main script.js (returned by getDashboardFilter())
One in listRenderer.js (updated by updateDashboardFilter())
The dashboard manager was reading from one variable but updating another:
getDashboardFilter() returned the main script's dashboardFilter (always stayed "all")
updateDashboardFilter() updated the list renderer's dashboardFilter
The Fix
I created a local updateDashboardFilter function in the main script that:
Updates the local dashboardFilter variable (the one getDashboardFilter() returns)
Calls the list renderer's updateDashboardFilter to keep both in sync
* Implement Export Function
Add export CSV functionality into Settings > System modal
* Fixed export button
Problem: The middleware/demo.js file was using ES6 export syntax, but the server was importing it using CommonJS require()
* export UI update
* Add simple csv export
* Removed placeholder logos with correct
Removed placeholder logos and placed real logo into public > assets > images
* remove white background from logo svg
* enlarge svg logo
* Include asset name with Warranty notifications
Add asset bane to warranty notifications so its formatted as:
⏰ Warranty Expiring in 7 days
Asset: Dell OptiPlex 7010
Model #: OptiPlex-7010
Warranty
Expires: 2024-01-15
🔗 View Asset: http://localhost:3000?ass=12345
Add new toast manager with stacking toasts with success and error colors and repositioned to top
set button loading to false on subasset form validation
revert index.html change
What Remains Intact:
✅ Warranty Notifications - Still sends notifications at 30, 14, 7, and 3 days before expiration
✅ Maintenance Frequency Notifications - Still sends notifications when frequency-based maintenance is due
✅ Maintenance Specific Date Notifications - Still sends 7-day advance and due-date notifications
✅ Duplicate Prevention - Still prevents duplicate notifications using tracking keys
✅ Data Validation - Still validates all maintenance events and dates
✅ Comprehensive Logging - Still provides detailed debug and summary logs
✅ Date Calculation Fix - Still uses the corrected start-of-day date comparison
The problem is that we're comparing the full DateTime (including time) instead of just the date portion. When the cron runs at 12:01 PM on the expiration date, the warranty expiration date (which defaults to midnight) is actually in the past by about 12 hours, resulting in a negative fractional day that gets floored to -1.
🔧 KEY ENHANCEMENTS:
Comprehensive Summary Reporting
Total assets/components checked
Total warranties processed
Success/failure notification counts
Current notification settings display
Robust Error Handling
Individual asset processing wrapped in try-catch
Failed notifications don't stop the entire process
Detailed error logging with context
Enhanced Date Parsing
Uses the same robust parseDate() function as maintenance
Proper timezone handling with TIMEZONE constant
Validation and warning for invalid dates
Safety Mechanisms
Expired warranty detection (1-7 days past expiration)
Asset type differentiation (Asset vs Component)
Consolidated threshold logic for cleaner code
Detailed Debug Logging
Individual warranty check logging
Processing status for each asset/component
Clear identification of notification triggers
CRITICAL ISSUES FIXED
1. TIMEZONE MISMATCH (HIGH RISK) - FIXED
Problem: Dates compared in UTC while cron runs in local timezone → off-by-one day errors
Solution: Implemented robust timezone handling using Luxon DateTime library
2. DATE PARSING FRAGILITY (HIGH RISK) - FIXED
Problem: Basic new Date() parsing could fail silently with invalid dates
Solution: Created comprehensive parseDate() function handling multiple formats with validation
3. DATE CALCULATION ERRORS (MEDIUM-HIGH RISK) - FIXED
Problem: Month arithmetic had edge cases (Jan 31 + 1 month = March 3 instead of Feb 29)
Solution: Implemented addTimePeriod() using Luxon's robust date arithmetic
4. MISSING VALIDATION & ERROR HANDLING - FIXED
Added comprehensive validation for all maintenance event data
Enhanced error handling to prevent system crashes
Added overdue detection as safety net (1-3 days past due notifications)
🛡️ SAFETY MECHANISMS ADDED
Overdue Detection: Catches missed maintenance (server downtime scenarios)
Duplicate Prevention: Tracking system prevents multiple notifications for same event
Data Validation: Invalid events are skipped with logging, don't crash system
Comprehensive Logging: Detailed debugging and summary reports
📊 TEST RESULTS
The comprehensive test suite shows all critical edge cases now work correctly:
✅ Leap year dates (Feb 29)
✅ Month-end arithmetic (Jan 31 + 1 month = Feb 29, not March 3)
✅ Timezone consistency across DST changes
✅ Multiple date formats parsed correctly
✅ Invalid dates handled gracefully
When viewing a main asset, sub-assets were rendered correctly using createSubAssetElement directly, so clicks worked fine
When viewing a sub-asset, sub-sub-assets were rendered using innerHTML, which converted the DOM elements to HTML strings and back to DOM, losing all event listeners
Instead of using innerHTML with HTML strings, I changed the code to:
Create DOM elements properly using document.createElement
Append elements directly using appendChild
- Automatically trims leading and trailing spaces from maintenance event names when rendering them in the Asset Details view because abite cant.
- Prevents excessive spacing between the "Event:" label and the event name, kinda works.
Clickable Tags in Asset List Sidebar
Tags in the asset list sidebar are now clickable
Clicking a tag sets the search input to filter by that tag
Added data-tag attributes and click event listeners