WatchMeGrow · Cameras

Camera health: two versions, and how they got here

A live board that tells a center admin, a regional manager, or an exec which cameras are down right now — built at two altitudes, and steered at each turn by specific calls rather than left to default choices.

Visual language from STYLE_GUIDE.md Light theme only 2 versions Offline · Degraded · Online · Out of date

How this took shape

Twenty-odd rounds of review, told as the six moves they actually amounted to. Every decision below was Franco's; this is where each one landed in the design.

1

The frame was set before any layout existed. Light theme only — the dark mockup as reference, not a target — a three-value legend, the scale to design for, and "a working prototype plus these notes" as the deliverable, all settled by answers rather than defaults before a single row was drawn. The feature was renamed from Current Status to Camera health in the same early pass, ahead of the fork, so that neither version would carry the old name.

2

Two versions were built to be compared, and then one was chosen. The second version came with its own brief — no center selected until one is chosen, problems collapsed by default, no streaming as a default state — and building both is what made the difference between them legible: version A nests camera health under its own rail item, version B introduces an account-level scope the product has never had. Several rounds then cut both of them back: the shared-recorder diagnosis thread came out, the problems-first / all-centers switch came out, and every route into a single center's camera page came out, on the grounds that a triage board should be an answer rather than a menu. Everything B pioneered was ported back into A, A was chosen as the first ship, and B was hidden rather than deleted — it still builds and deploys, it is simply no longer linked.

3

Camera health got its own place in the product — and the prototype deliberately starts somewhere else. Monitoring became a top-level rail item rather than a sub-tab of Cameras, because "camera health across every center" is a different kind of question from anything a center-scoped section answers. And the prototype opens on the Dashboard, since a new section can only be judged by arriving at it: you watch the header's center picker empty and lock as you cross over, instead of reading that it does. Later, screenshots of the real product turned that dashboard from a sketch into the actual screen — welcome band, stat cells, quick tip, a camera panel rendering that center's real statuses, and a phone bar where the search field takes over the entire top row.

4

The board is about the whole assignment, and the centers strip is what says so. Every assigned center as one block, in the same encoding the cameras use a level down, grouped by brand so the hunt for one site is bounded — and sharing a single card with the search row, because clicking a block writes that center into the field directly beneath it. Once the strip existed it made the This center / All centers toggle redundant, and the toggle went. The assignment grew to a regional manager's real list — 73 centers, eight brands, fifty states and six provinces — which is what justifies the rest of the machinery: brand grouping, pagination, saved views, and a search box that holds a draft until you submit it rather than querying on every keystroke.

5

Sorting and filtering became real machinery, specified from how the work is described out loud. Five sorts, each with a direction and a natural default; nine facets, including the ones that came from district managers ("what other schools in my region have issues") and the operational ones nobody asks for until they need them — payment model, timezone for maintenance windows. The sequencing question — should the filters funnel — is answered no, with one exception in Filtering as a funnel below: every facet's options are computed from the centers passing your other choices, so the data does the funnelling instead of the user. Within a category values are ORed, across categories ANDed. And an ⓘ in both panels opens one page stating every formula, because a column called "Defect Rate" is worth nothing if two people read it differently.

Two habits did most of the work

Cut the second copy. Almost every round ended in a subtraction, and almost always because something on screen already said it: the "N need attention" line beside the strip title (the first KPI says it), the camera legend above the list (the strip's legend covers the same encoding), the arrow from a picked block to the search field (the highlight and the filled field already say it), the coloured left edge on rows (the counts and the squares state severity twice already), the comfortable/compact switch, the density controls, the link icon beside every center name, the "all clear" banner, and the LIVE / IMPAIRED badges on the camera modal — which also promised a stream that panel never shows.

Two of those subtractions came last, and both were larger than a line of text. Per-center annotations — a scratch textbox, a presence filter, a sort on last-edited, a chip on the row and a dot on the strip — came out whole. They were a well-built answer to a question this screen was not being asked: camera health is a triage board read in seconds, and the moment it also stores free text it owes someone a history, an author and a place to reply, none of which belong here. And the Overview toggle went with it. A control whose only job is to hide the thing the page is named after is a confession that the thing is too big; the honest fix is to make it fit, which is the whole argument of the severity band. "Your centers" is now simply always there.

One phrase, one meaning. The slower discipline, and the one that produced the last round: a word used in two places has to mean the same thing in both, or the board quietly teaches people two different products. Sort labels now name a field and never an order, because a label carrying a direction is wrong in one of the two directions. "All cameras reporting" belongs to the green square and nothing else — the stricter filter option had to be renamed to stop borrowing it. And the headline count, the strip, the refresh announcement and the screen-reader labels all now answer "how many of my centers are in trouble" with the same number, which they did not before the colour rule changed underneath them.

The last rounds, in full

Mostly definitions rather than features. A center's colour became a verdict with two precise ends: red only when every camera is dark, green only when nothing is offline and nothing impaired, orange for everything else. It went through coverage-only first — green tolerating degraded cameras — and came back, because a green square that could still hide a problem put the strip out of step with the filter and the headline count. The rule is stated beside the legend it governs rather than left to be inferred. Sort labels went neutral — "Longest Outage" became Outage Time — with the direction saying which end you want. Filtered-out centers are locked rather than clickable: the strip used to answer a click on a dimmed block by tearing down the filters that dimmed it, which is a strange thing for a greyed control to do, so it now nudges and says why instead. And the search field states its job once, in a placeholder that no longer changes underneath the reader.

A review pass then closed four gaps. The State list was interleaving Alberta between Alaska and Arizona; it is grouped under country headings now, and still not gated, because a state name is unambiguous on its own where a city name is not — Springfield and Rochester are each two different centers here, which is why City is gated and State is not. Timezones are written out rather than abbreviated, since the reason to filter on one is scheduling work outside opening hours and a two-letter code makes the reader do the decoding. The camera modal gained a toggle between how long a camera has been down and the wall-clock moment it went down — four hours sizes the problem, 2:14 AM is what you match against a power cut — and lost the camera ID row. And the KPI row stopped repeating itself: its first tile and its second were running the same filter, so the first now counts centers with anything wrong and the second stays on cameras offline.

Then the fourth camera state arrived — out of date — with the assessor's real rules behind all four: an HLS playlist that does not exist, FFMPEG restarts past a critical or a warning threshold, an HLS playlist or a JPG older than a hundred seconds, and (for the fourth) an assessment older than an hour. Those rules now live on the reference page beside the center rules, because a board that claims a camera is degraded owes the reader the definition it used. The state itself is drawn as a veil rather than a colour, and filtered as a fifth Status value, Has stale cameras. That last one was a reversal: freshness started in a category of its own, on the argument that ANDing it against a status is a question worth being able to ask — "which centers have offline cameras and readings I can't trust". One more category in the rail turned out to cost more than that query is worth, so the two live together and combine with OR like every other pair inside a category. The complement that matters is untouched: Has any issue and All cameras healthy still partition the strip exactly, and the new value simply overlaps both.

Two versions

A · Nested — a sub-nav under its own rail item

Camera health as the only sub-tab of a dedicated Monitoring rail item — originally filed under Cameras, and originally shown beside four placeholder sub-tabs that have since gone: a section that holds one thing should say so rather than advertise a roadmap. Cheapest to ship of the two. It no longer carries a scope toggle either: the page is always the whole assignment, the header's center picker empties and greys out while you are in it, and a strip of all 73 centers makes the altitude self-evident. The prototype opens on a center's Dashboard so that change of state is something you watch happen rather than something you read about.

B · Toggle — a scope above center selection

Camera health as the one thing that exists at account altitude, reached by a toggle in the header's search field. No center selected, center-scoped nav hidden. Resolves the seam honestly, at the cost of a scope the app has never had before.

B is the right long-term model; A is the right first ship. If the toggle version is likely within two quarters, build B now — retrofitting a scope later means re-teaching the navigation twice.

Layout

One row per center in both versions: identity, a fixed column of aligned status counts, then a wrapping grid of camera squares. Status is colour plus texture, never colour alone — red and green is the pair that fails for red-green colour blindness, and this board's whole meaning rides on exactly that pair.

Offlinecross Degradeddiagonal hatch Onlineflat fill Out of datefaded, dashed ring

The fourth one is not a fourth status. Offline, degraded and online are the assessor's verdict on a camera and are stored with the reading. Out of date is this page's verdict on the readingassessed_at older than an hour, against an assessor that runs every thirty minutes, so two consecutive runs produced nothing. It is computed in the browser and written nowhere.

That distinction decides how it draws. "This camera was offline, and we have not heard since" is two facts, and a fourth flat colour can only show one of them — it would overwrite the last thing anyone actually knew in order to say that we no longer know it, which is the wrong half to keep. So staleness is a veil: the block keeps its colour and its shape, drops its saturation, and gains a dashed ring. Both facts survive, and the shape encoding means offline and degraded stay distinguishable even when the hue is almost gone.

The counts get their own column because sharing it with identity makes them wrap unpredictably; given a column, ten rows becomes one scannable ladder of digits. Rows carry no coloured left edge: the counts column and the squares already state the severity twice, and a third encoding of it was just stripes.

Above the list, version A adds a centers strip: the same three-way encoding applied one level up, one block per center the account can see, grouped by brand so the hunt for a particular site is bounded. It is not a separate card — it shares one card with the search and filter row, because its real job is navigational: clicking a block writes that center's name into the field below it and the list narrows to that center. Two rules keep that promise honest. Filters win: a block the filters exclude is dimmed and locked, and clicking it explains itself rather than searching — an earlier version answered that click by silently dropping the filters that excluded the center, which made a greyed control behave like a reset button. And the search text is excluded from the strip's own dimming — filters dim the blocks they rule out, but letting the search dim them would black out seventy-two blocks the moment you clicked the seventy-third, which is the opposite of what an overview is for.

The three colours in the strip answer a different question from the three inside a center. A camera is offline, degraded or normal — three states of one device. A center is a verdict on the whole site, and only its two ends are precise: green means nothing offline and nothing impaired, red means every camera dark, and orange is everything in between. That middle bucket is deliberately ungraded. One soft camera among nineteen good ones and nine dark cameras out of ten land on the same colour, because the square answers does this need looking at and the row underneath answers how much — a hue asked to separate those two would be a number pretending to be a colour. An ⓘ beside the legend states the rule where it applies.

Staleness never reaches the center rule. It was briefly allowed to — a center faded once every camera in it was out of date — and that turned out to be one idea too many in a component whose entire job is to answer one question at a glance. A center square says how much of a site is working; how old a reading is asks a different question of a different object, and the veil belongs where the object is. So the strip stays a three-state overview, and out-of-date shows on the camera blocks inside a center, on the count beside them, and in the Has stale cameras status filter. A banner above the board announcing the fleet-wide count went in and came straight back out: staleness is a caveat on a reading, and a caveat does not earn a permanent alarm strip above the numbers it qualifies.

Keeping green strict is what makes the rest of the board agree with itself. Green is the exact complement of the Has any issue filter, so the count of non-green squares, the first KPI, the status filter and the refresh announcement are all one number rather than four readings of a similar idea. An earlier version let green tolerate degraded cameras — coverage rather than health — and every one of those four drifted apart from the others by the number of quality-only centers.

The prototype's user is assigned 73 centers across eight brands. At that size the strip becomes two rows of brand-labelled groups rather than an undifferentiated wall, which is why the grouping exists at all: it bounds the hunt for one site to a handful of squares. Center rows sit at one density — the comfortable one, with 20px squares — since a density switch is a preference the page can simply make well once. The board fills the window's full width: the camera grids are what benefits from every pixel, because a wider row is fewer wrapped rows per center.

What's different between them

Both versions now agree on more than they did: every center is always listed, saved views exist in both, and neither one filters the page down to a single center. What remains different is how each one states its altitude, and where the detail opens.

There is no center view. B has exactly two screens: the cold-start dashboard and camera health. Center names are labels, not links; the camera modal offers my.wmg and nothing else. This is the sharper reading of "camera health is the only place to check cameras" — if drilling into a single center's camera page were one click away, the board would be a menu rather than the answer.

Filtering as a funnel

The question was whether these filters should sequence — country, then state, then city, then the rest. The answer here is no, with one exception, for two reasons.

A funnel charges everyone for a narrowing nobody asked for. The assignment is already a subset: these are the centers this person is responsible for. Making them choose a country and a state before the board will show anything adds three clicks to reach the view they had on arrival. Filters should subtract from a complete board, not assemble one.

What genuinely depends on something else is the options, not the order. Every facet's list is built from the centers that pass your other choices, and each option carries the count it would leave. So choosing Texas reduces the city list to Texas cities without anyone walking a hierarchy, and an option that would empty the board is greyed out rather than offered. The data does the funnelling.

The exception is City, which is gated on a State — ungated it lists every city in the assignment, which is a wall rather than a filter. The rail says so in place of the value ("after state") instead of hiding the row, so the dependency is discoverable rather than invisible. Country is treated as the outermost scope in one respect only: changing it drops any state, city, region or brand it leaves with nothing to match, since those combinations are guaranteed-empty rather than merely narrow.

Everything else is a flat facet on purpose. Brand, payment model, parent access and timezone all cut across geography — a brand spans states, a timezone spans half the country — so nesting them under a place would be a lie about the data. Regions group states, which makes region and state two altitudes of the same question rather than two steps of one: a district manager starts at their region, a compliance officer starts at a state, and neither should have to pass through the other.

The refresh contract

Keyboard and access

Notes for engineering

Open questions

1

Should the Camera health toggle carry a count?

The concept spec called a badge the main discoverability driver on a good day; the reference image had none, so it shipped without one. A middle option: show the number only when non-zero.

2

Should an out-of-date reading still count toward Defect Rate and Defect Count?

Today it does: a camera last assessed as offline five hours ago is still counted as offline, on the grounds that it is the best information available. The alternative is to exclude stale rows from both metrics, which makes the numbers honest but makes them move for a reason that has nothing to do with the cameras. Worth deciding before either number is used as a target.

3

Where does someone go once camera health tells them a center is broken?

B deliberately has no route into a single center, so the next step happens outside this screen — on my.wmg, or by leaving camera health and picking the center the normal way. Worth confirming that matches how the work actually continues, since it is the one thing the board stops short of.

4

Timestamps in the viewer's timezone, or the center's?

Relative times ("27m ago") dodge the question for now; absolute timestamps will need an answer.

5

Is camera order within a center stable and meaningful?

If not, ordering by room would beat whatever order the log happens to return.

6

Does "degraded" carry a reason?

Low bitrate versus dropped frames versus a bad angle is the difference between calling the vendor and nudging the camera.