Add USB, Notifications, Network plugins and reusable EmployeeSearch component

New Plugins:
- USB plugin: Device checkout/checkin with employee lookup, checkout history
- Notifications plugin: Announcements with types, scheduling, shopfloor display
- Network plugin: Network device management with subnets and VLANs
- Equipment and Computers plugins: Asset type separation

Frontend:
- EmployeeSearch component: Reusable employee lookup with autocomplete
- USB views: List, detail, checkout/checkin modals
- Notifications views: List, form with recognition mode
- Network views: Device list, detail, form
- Calendar view with FullCalendar integration
- Shopfloor and TV dashboard views
- Reports index page
- Map editor for asset positioning
- Light/dark mode fixes for map tooltips

Backend:
- Employee search API with external lookup service
- Collector API for PowerShell data collection
- Reports API endpoints
- Slides API for TV dashboard
- Fixed AppVersion model (removed BaseModel inheritance)
- Added checkout_name column to usbcheckouts table

Styling:
- Unified detail page styles
- Improved pagination (page numbers instead of prev/next)
- Dark/light mode theme improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-01-21 16:37:49 -05:00
parent 02d83335ee
commit 9c220a4194
110 changed files with 17693 additions and 600 deletions

View File

@@ -11,9 +11,10 @@ import click
from shopdb.plugins.base import BasePlugin, PluginMeta
from shopdb.extensions import db
from shopdb.core.models.machine import MachineType
from shopdb.core.models import AssetType
from .models import PrinterData
from .api import printers_bp
from .models import PrinterData, Printer, PrinterType
from .api import printers_bp, printers_asset_bp
from .services import ZabbixService
logger = logging.getLogger(__name__)
@@ -21,11 +22,15 @@ logger = logging.getLogger(__name__)
class PrintersPlugin(BasePlugin):
"""
Printers plugin - extends machines with printer-specific functionality.
Printers plugin - manages printer assets.
Printers use the unified Machine model with machinetype.category = 'Printer'.
This plugin adds:
- PrinterData table for printer-specific fields (windowsname, sharename, etc.)
Supports both legacy Machine-based architecture and new Asset-based architecture:
- Legacy: PrinterData table linked to machines
- New: Printer table linked to assets
Features:
- PrinterType classification
- Windows/network naming
- Zabbix integration for real-time supply level lookups
"""
@@ -46,7 +51,7 @@ class PrintersPlugin(BasePlugin):
"""Return plugin metadata."""
return PluginMeta(
name=self._manifest.get('name', 'printers'),
version=self._manifest.get('version', '1.0.0'),
version=self._manifest.get('version', '2.0.0'),
description=self._manifest.get(
'description',
'Printer management with Zabbix integration'
@@ -58,12 +63,21 @@ class PrintersPlugin(BasePlugin):
)
def get_blueprint(self) -> Optional[Blueprint]:
"""Return Flask Blueprint with API routes."""
return printers_bp
"""
Return Flask Blueprint with API routes.
Returns the new Asset-based blueprint.
Legacy Machine-based blueprint is registered separately in init_app.
"""
return printers_asset_bp
def get_models(self) -> List[Type]:
"""Return list of SQLAlchemy model classes."""
return [PrinterData]
return [
PrinterData, # Legacy Machine-based
Printer, # New Asset-based
PrinterType, # New printer type classification
]
def get_services(self) -> Dict[str, Type]:
"""Return plugin services."""
@@ -82,16 +96,63 @@ class PrintersPlugin(BasePlugin):
"""Initialize plugin with Flask app."""
app.config.setdefault('ZABBIX_URL', '')
app.config.setdefault('ZABBIX_TOKEN', '')
# Register legacy blueprint for backward compatibility
app.register_blueprint(printers_bp, url_prefix='/api/printers/legacy')
logger.info(f"Printers plugin initialized (v{self.meta.version})")
def on_install(self, app: Flask) -> None:
"""Called when plugin is installed."""
with app.app_context():
self._ensureprintertypes()
self._ensure_asset_type()
self._ensure_printer_types()
self._ensure_legacy_machine_types()
logger.info("Printers plugin installed")
def _ensureprintertypes(self) -> None:
"""Ensure basic printer machine types exist."""
def _ensure_asset_type(self) -> None:
"""Ensure printer asset type exists."""
existing = AssetType.query.filter_by(assettype='printer').first()
if not existing:
at = AssetType(
assettype='printer',
plugin_name='printers',
table_name='printers',
description='Printers (laser, inkjet, label, MFP, plotter)',
icon='printer'
)
db.session.add(at)
logger.debug("Created asset type: printer")
db.session.commit()
def _ensure_printer_types(self) -> None:
"""Ensure basic printer types exist (new architecture)."""
printer_types = [
('Laser', 'Standard laser printer', 'printer'),
('Inkjet', 'Inkjet printer', 'printer'),
('Label', 'Label/barcode printer', 'barcode'),
('MFP', 'Multifunction printer with scan/copy/fax', 'printer'),
('Plotter', 'Large format plotter', 'drafting-compass'),
('Thermal', 'Thermal printer', 'temperature-high'),
('Dot Matrix', 'Dot matrix printer', 'th'),
('Other', 'Other printer type', 'printer'),
]
for name, description, icon in printer_types:
existing = PrinterType.query.filter_by(printertype=name).first()
if not existing:
pt = PrinterType(
printertype=name,
description=description,
icon=icon
)
db.session.add(pt)
logger.debug(f"Created printer type: {name}")
db.session.commit()
def _ensure_legacy_machine_types(self) -> None:
"""Ensure basic printer machine types exist (legacy architecture)."""
printertypes = [
('Laser Printer', 'Printer', 'Standard laser printer'),
('Inkjet Printer', 'Printer', 'Inkjet printer'),