Work-order codes no longer overlap (#42), Materials before Heat map (#44), 60 min idle for Admin and Clerical (#45)
ci / frontend (pull_request) Successful in 54s
ci / server (pull_request) Failing after 12m12s
ci / image (pull_request) Skipped
ci / e2e (pull_request) Skipped

- Traveler: each work-order line shrinks, then clips, to fit its own box; 18 pt gap between boxes
- Work orders tab: card text wraps inside the card, smaller work order, wider cards
- Packet tabs: Materials before Heat map
- Sessions: SESSION_DESK_MINUTES (60) for Admin and Clerical lock and sign-out; /me answers
  lock_minutes and the browser's idle lock follows it

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-21 13:16:26 -06:00
co-authored by Claude Opus 5.5
parent b0f6a09173
commit de5b361d85
14 changed files with 119 additions and 36 deletions
+17 -2
View File
@@ -28,6 +28,18 @@ log = logging.getLogger(__name__)
LAST_SEEN_SLACK = timedelta(seconds=30)
def desk_roles(roles: list[str]) -> bool:
"""Admin and Clerical get the longer desk idle limits."""
return any(r == "admin" or r.startswith("clerical") for r in roles)
def idle_minutes(s: Settings, roles: list[str]) -> tuple[int, int]:
"""(sign-out, screen lock) idle minutes for a session with these roles."""
if desk_roles(roles):
return max(s.session_idle_minutes, s.session_desk_minutes), max(s.session_lock_minutes, s.session_desk_minutes)
return s.session_idle_minutes, s.session_lock_minutes
# ---------------------------------------------------------------- plumbing
@@ -160,6 +172,7 @@ class Principal:
kind: str
device: dict[str, Any] | None
ip: str | None
lock_minutes: int = 3
@property
def session_id(self) -> uuid.UUID:
@@ -253,11 +266,12 @@ def resolve(request: Request, d: Db, *, allow_locked: bool, token: str | None =
raise ApiError(401, "no_session", "Not signed in.")
now = row["db_now"]
idle = now - row["last_seen_at"]
if idle > timedelta(minutes=s.session_idle_minutes):
idle_min, lock_min = idle_minutes(s, list((row.get("roles_snapshot") or {}).get("roles", [])))
if idle > timedelta(minutes=idle_min):
_expire(conn, row, "idle", ip)
raise ApiError(401, "session_expired", "Signed out after a period of no activity.")
lock_source: str | None = None
if row["locked_since"] is None and idle > timedelta(minutes=s.session_lock_minutes):
if row["locked_since"] is None and idle > timedelta(minutes=lock_min):
lock_source = "idle"
hidden_since = row.get("hidden_since")
if hidden_since is not None:
@@ -308,6 +322,7 @@ def resolve(request: Request, d: Db, *, allow_locked: bool, token: str | None =
kind=str(row["kind"]),
device=device,
ip=ip,
lock_minutes=idle_minutes(s, roles)[1],
)