Skip to content

Mobile Friendly Problem Management

Ed Mozley edited this page Aug 25, 2026 · 3 revisions

Mobile: Problem Management

Problem Management is the seventh module made mobile‑friendly, after Tickets, Assets, Calendar, Knowledge, Service Status and Watchtower. Same hard rule: one @media (max-width: 768px) block, desktop byte‑identical.

🧰 Code‑level catalogue of the techniques: Mobile: Techniques & Tricks.

Shipped as #1181, commit 854e3532. LAYER 22.


Scope

Page State
problem-management/ β€” list, detail and modals Done β€” sidebar β†’ header strip, filters β†’ chip row, pane hiding, modal sheets (LAYER 22)
problem-management/settings/ Done β€” inherits the shared settings layers
problem-management/help.php Done β€” inherits 16h from the Calendar round

The worst starting point so far

viewport = 360   docScrollW = 872   CONTAINED = false
sidebar  = 250px          .pm-main = 110px

Two separate problems, and only one of them was the obvious one.

872px wide is the iOS reflow‑to‑desktop trap, and β€” exactly as with Watchtower β€” the cause was the shared header row rather than anything in this module. Opting in fixed it outright.

The 110px list did not move. A 250px fixed sidebar beside a flexible pane leaves the pane whatever is left, and on a 360px screen that is a third of the width for the actual content. The page was contained, scrollable, and useless.

πŸ”‘ This is the clearest demonstration yet that docScrollW === innerWidth is the floor, not the finish line. Every automated check passed on a screen showing a 110px column of problems.


LAYER 22

The sidebar becomes a header strip

It holds three things: Search, New problem, and the status filters. Stacked as‑is that is ~250px of a 640px screen before the first problem appears.

.pm-sidebar {
    display: grid;
    grid-template-columns: 1fr 1fr;   /* the two buttons share a line */
    gap: 8px;
    border-right: none;
    border-bottom: 1px solid var(--border, #e5e7eb);
}
.pm-sidebar h3,
.pm-sidebar #pmStatusFilters { grid-column: 1 / -1; }   /* these span both */

A two‑column grid with selective spanning is a tidy way to say "these two side by side, everything else full width" without adding a wrapper element to the markup.

Filters become a chip row, counts intact

#pmStatusFilters {
    flex-direction: row;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
}
#pmStatusFilters::-webkit-scrollbar { display: none; }
.pm-filter { flex: 0 0 auto; white-space: nowrap; }

Same scrollbar‑hiding idiom as .detail-tabs, .sw-filter-tabs and the settings .tabs β€” the bar is redundant on touch and eats a row of height on a narrow desktop window.

Why not a <select>? Because each chip carries its count, and the counts are half the value of the control β€” "are there any open ones at all?" is answered without opening anything. A dropdown hides exactly the information you came for.

The module's own modal class

.pm-modal-content { width: 100%; max-width: none !important; margin: 0; border-radius: 0; min-height: 100%; }

LAYER 3 promotes every .modal-content to a full‑screen sheet β€” but this module has its own .pm-modal-content, so none of that reached it. Three dialogs (link incidents, link change, detect problems) stayed as 92%‑width centred boxes with a 40px margin.

πŸ”‘ Check the module's modal class before assuming LAYER 3 covers it. A module that rolled its own is invisible to a rule keyed on the shared name, and there is nothing in the measurements to tell you β€” a centred box on a phone is contained.


The JS branch, and why this module earned one

Three modules in a row shipped with no mobile.js at all, which makes a fourth feel like failure. It is not β€” the test is what the page actually does.

This module was already master‑detail: pmOpenDetail() hides #pmListView and shows #pmDetailView in the same pane. Nothing needed restructuring. The only fault was that the sidebar stayed on screen while you read a problem, spending ~140px of a 640px screen on controls for the list you had just left.

var openDetail = window.pmOpenDetail;
window.pmOpenDetail = function () {
    var out = openDetail.apply(this, arguments);
    Promise.resolve(out).then(function () {
        var dv = document.getElementById('pmDetailView');
        if (dv && dv.style.display !== 'none') setPane('detail');
    });
    return out;
};

Both functions are top‑level declarations, so they are properties of the global object and can be wrapped from mobile.js with no edit to problem-management.js β€” see Techniques Β§1.

Two details worth copying:

  • Mark the pane only after the promise resolves. pmOpenDetail is async and bails on a failed fetch. Marking up front would leave the sidebar hidden behind an error toast, with the list underneath and no way back to it.
  • The state is an attribute, the hiding is CSS. body[data-pm-pane="detail"] .pm-sidebar { display: none } sits inside the @media block, and the wrapper refuses to set the attribute unless mq.matches β€” so desktop never sees it even if the JS runs.

Verification

Check Result
List / detail / modal, each at 360px βœ… contained, zero uncontained elements
Pane cycle: list β†’ detail β†’ back βœ… sidebar grid β†’ none β†’ grid
Help and Settings at 360px βœ… contained, zero uncontained
Desktop control @ 1100 / 1200 / 1400px sidebar 250px throughout, body not flex
data-pm-pane on desktop βœ… never appears, at any point in the same cycle
mobile.js parse check βœ… PARSED_OK β€” with a negative control

That last row matters. A parse harness that cannot fail proves nothing, so the same page was loaded with a deliberately broken script alongside: it reported Uncaught SyntaxError, which is what makes the clean result meaningful.

Driving all three states β€” not just the page as it loads β€” is the reason the modal fault was found at all. A page that renders one view at a time is one‑view verified.


Known rough edges

  • The AI "Detect problems" suggestions list ticket numbers as plain text. They come from a model rather than the database, so linking them needs a server‑side numberβ†’id resolve that would also quietly validate the model's output β€” see discussion #91. Not a mobile issue, but it shows up here.
  • The detail view's field grid is two columns even at 360px (PRIORITY / ASSIGNED TO). It reads fine with short values; a long assignee name would be tight.

Round 2 (#1182) β€” the detail view's tables

Ed found the History panel's What column running off the right-hand edge. Measuring the other two tables on the same page found the same fault in a quieter and worse form.

Table Measured at 360px
History 345px in a 328px section spills β€” the half a person notices
Linked incidents 294px, "fits" subject cell 82 Γ— 134px β€” seven lines for one sentence
Fix (linked change) same shape same

πŸ”‘ Overflow gets reported; crushing does not. The two that squeezed passed every automated check β€” contained, nothing uncontained, no overflow β€” while being less readable than the one that visibly broke.

All three became card feeds, by the rule now written up as Techniques Β§11: every one of them has a column holding prose (What, Subject, Title), and a sentence can be neither scrolled sideways nor squeezed into 82px.

History now reads the way the module's own notes already do β€” a quiet when Β· who line, then the change itself:

15/07/2026, 01:17:50 Β· Administrator
changed linked_incident to "LUN-779-88063"

Two details worth copying:

  • The meta cells are display: inline, not a flex row. Flex blockifies its items, so a flex column cannot put two cells on one line. Plain inline on two adjacent cells does.
  • :has(.pm-when) scopes the history-only rules. Three tables share the .pm-table class; that one class appears only on audit rows, so the markup needed no change at all to tell them apart.

⚠️ The empty state is a single <td colspan> β€” the first and last child of its row β€” so any rule keyed on either position swallows it. Asserted explicitly: "No change linked yet." still renders after the change.

Desktop control: display: table, table-header-group, table-row, table-cell, header row visible, four columns, at 1100px and 1400px.

Reference

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally