Add dashboard defaults (visitor-IP -> business-unit) for kiosk displays

Classic feature gap: a shopfloor/lobby kiosk auto-selects which business unit to
show based on the display PC's IP (classic dashboarddefaults table +
apivisitorlocation.asp). For the main admin dashboard this does nothing - it is
kiosk/visitor-display infra.

- Model: DashboardDefault (dashboarddefaults: ipaddress unique, businessunitid
  FK, description). Migration 7d01_dashboarddefaults (head).
- API (core, /api/dashboarddefaults): CRUD + GET /visitor-location that resolves
  the calling display's business unit from its IP (X-Forwarded-For/remote_addr,
  or explicit ?ipaddress=); unmapped IP returns a null businessunitid, not an
  error. Unauthenticated resolve (kiosks); writes are admin.
- Frontend: ShopfloorDashboard auto-selects its business unit via visitor-location
  on load when none is chosen; Settings > Dashboard Defaults CRUD page +
  dashboardDefaultsApi client.

Tests: create + resolve by IP -> BU; unmapped IP -> null; duplicate IP 409.
191 tests pass, naming green, app boots, endpoint + admin page verified live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-06-26 22:09:38 -04:00
parent 85f98e87cb
commit a4b98e10db
12 changed files with 475 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ from .machine import MachineType
from .vendor import Vendor
from .model import Model
from .businessunit import BusinessUnit
from .dashboarddefault import DashboardDefault
from .location import Location, LocationType
from .operatingsystem import OperatingSystem
from .relationship import AssetRelationship, RelationshipType
@@ -30,6 +31,7 @@ __all__ = [
'Vendor',
'Model',
'BusinessUnit',
'DashboardDefault',
'Location',
'LocationType',
'OperatingSystem',

View File

@@ -0,0 +1,28 @@
"""Dashboard default model: visitor-IP -> business-unit mapping.
A shopfloor / lobby kiosk display resolves which business unit to show by the
display PC's IP address. Powers the visitor-location lookup the shopfloor and
TV dashboards call when no explicit business unit is given.
"""
from shopdb.extensions import db
from .base import BaseModel
class DashboardDefault(BaseModel):
"""Maps a display PC IP address to the business unit it should show."""
__tablename__ = 'dashboarddefaults'
dashboarddefaultid = db.Column(db.Integer, primary_key=True)
ipaddress = db.Column(db.String(50), unique=True, nullable=False)
businessunitid = db.Column(
db.Integer,
db.ForeignKey('businessunits.businessunitid'),
nullable=False
)
description = db.Column(db.String(255))
businessunit = db.relationship('BusinessUnit')
def __repr__(self):
return f"<DashboardDefault {self.ipaddress} -> {self.businessunitid}>"