@bongos/core 1.21.33 → 1.21.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.bongos-core.json +51 -41
  2. package/docs/copy-inventory.md +19 -17
  3. package/docs/copy-registry.json +39 -21
  4. package/docs/module-api-changelog.md +4 -0
  5. package/docs/page-inventory.json +11 -1
  6. package/docs/page-readings.json +322 -194
  7. package/modules/autonomy/governor-docket.js +68 -1
  8. package/modules/government/docket-floors.js +20 -0
  9. package/modules/government/docket-vocab.js +14 -0
  10. package/modules/government/docket.js +103 -2
  11. package/modules/government/routes/government.js +13 -0
  12. package/modules/hall-ui/public/street-card.js +97 -1
  13. package/modules/hall-ui/public/street-desk.js +386 -0
  14. package/modules/hall-ui/public/street-inside.js +14 -10
  15. package/modules/hall-ui/public/street.css +60 -0
  16. package/modules/hall-ui/public/street.html +9 -3
  17. package/modules/hall-ui/public/street.js +238 -17
  18. package/modules/hall-ui/public/street.states.json +314 -6
  19. package/modules/lifecycle/governor-docket.js +24 -2
  20. package/modules/lifecycle/routes/project-settings.js +24 -0
  21. package/modules/onboarding/routes/access-requests.js +7 -0
  22. package/modules/provisioning/core-upgrade.js +1 -1
  23. package/modules/provisioning/routes/core-upgrade.js +36 -1
  24. package/modules/security/routes/reports.js +23 -0
  25. package/package-lock.json +2 -2
  26. package/package.json +1 -1
  27. package/release-notes.json +12 -0
  28. package/src/bongos/routes/backup.js +26 -0
  29. package/src/bongos/routes/builders.js +40 -0
  30. package/src/bongos/routes/modules.js +18 -0
  31. package/src/module-api.js +1 -1
  32. package/tests/governor_docket.mjs +28 -1
  33. package/tests/hall_matter_card.mjs +12 -3
  34. package/tests/hall_street.mjs +1 -1
  35. package/tests/hall_street_amend.mjs +1 -1
  36. package/tests/hall_street_desk.mjs +557 -0
  37. package/tests/hall_street_inside.mjs +1 -1
@@ -41,6 +41,16 @@
41
41
  // request, says the answer in a toast, re-reads the feeds so the pins, the
42
42
  // readings and the evening follow, and marks the building that answered (a CSS
43
43
  // class for 1.6 s: it rises or lights, and the matter's pin settles).
44
+ //
45
+ // THE FLOOR DESK (task 1004558). The chosen floor's block is the desk from the
46
+ // round 2 board (Desks.dc.html): GovDesk.deskModel() (street-desk.js) gives the
47
+ // header, tiles, rows, quiet state and record line under the line rule, and
48
+ // this file draws it. A row's buttons send the matter card's own request
49
+ // (GovMatterCard.request, the same routes), so acting happens in place. The
50
+ // game layer: the today meter in the street header (one pip per decision, kept
51
+ // for the day in this browser), a row that clears away (a CSS class, then the
52
+ // redraw), a cleared floor that lights its windows and shows a tick in the
53
+ // drawing, and the toast after every act.
44
54
 
45
55
  (function () {
46
56
  'use strict';
@@ -70,6 +80,11 @@
70
80
  var data = { docket: null, goals: [], rollups: [], earnings: null, loaded: false, tasks: {}, room: null, constitution: null };
71
81
  var card = { cv: null, form: null, error: '', busy: false, fresh: null, answer: null };
72
82
  var view = { open: null, opener: null, model: null, scale: 1, mode: null, inside: null, insideModel: null };
83
+ // the desk's own state: rows you acted on this visit, what you did per floor,
84
+ // the floors you cleared, the row whose facts or chips are open
85
+ var desk = { done: {}, mine: {}, cleared: {}, open: null, ask: null, busy: false, roomAsked: false, model: null };
86
+ var TODAY_KEY = 'gov-desk-today';
87
+ var today = { day: '', n: 0 };
73
88
 
74
89
  // ---------- light / dark ----------
75
90
  var chosen = null;
@@ -343,6 +358,7 @@
343
358
  var eveLine = $('streetEveningLine');
344
359
  if (eveLine) eveLine.textContent = m.eveningLine;
345
360
 
361
+ drawToday();
346
362
  $('hudNeeds').textContent = m.hud.needs;
347
363
  $('hudMoving').textContent = m.hud.moving;
348
364
  $('hudShare').textContent = m.hud.share;
@@ -392,7 +408,18 @@
392
408
 
393
409
  function drawBuilding(mode) {
394
410
  var st = view.inside;
395
- var bv = I.buildingModel({ key: st.key, docket: data.docket, goals: data.goals, mode: mode, now: Date.now(), floor: st.floor, drawer: st.drawer, picks: st.picks });
411
+ var D = window.GovDesk;
412
+ // a building opens on the floor that needs you first (the desk's work)
413
+ var startFloor = st.floor == null && D && data.docket ? D.firstNeeds(st.key, data.docket) : null;
414
+ var cleared = [];
415
+ Object.keys(desk.cleared).forEach(function (k) { var p = k.split(':'); if (p[0] === st.key) cleared.push(Number(p[1])); });
416
+ var bv = I.buildingModel({ key: st.key, docket: data.docket, goals: data.goals, mode: mode, now: Date.now(), floor: st.floor == null ? startFloor : st.floor, drawer: st.drawer, picks: st.picks, cleared: cleared });
417
+ // a sitting's ballot is read once, so its row can vote in place
418
+ var C = window.GovMatterCard;
419
+ if (!desk.roomAsked && !data.room && C && data.docket && data.docket.matters.some(function (x) { return x && x.building === st.key && C.needsRoom(x); })) {
420
+ desk.roomAsked = true;
421
+ loadRoom().then(function () { if (view.inside) draw(); });
422
+ }
396
423
  view.insideModel = bv;
397
424
  setText('insideKicker', bv.kicker);
398
425
  show('insidePanel', true); show('insideDHead', false); show('insideLegend', false);
@@ -481,7 +508,7 @@
481
508
  // the chosen floor: two blocks that take turns, so the block re-enters on every pick
482
509
  var on = $(bv.flipA ? 'insideOpenA' : 'insideOpenB'), off = $(bv.flipA ? 'insideOpenB' : 'insideOpenA');
483
510
  if (off) { off.hidden = true; clear(off); }
484
- if (on) { on.hidden = false; fillOpen(on, bv); }
511
+ if (on) { on.hidden = false; fillDesk(on, bv); }
485
512
  setText('insideOnFile', bv.onFile);
486
513
  var drawers = $('insideDrawers'); clear(drawers);
487
514
  bv.drawers.forEach(function (dw) {
@@ -505,28 +532,220 @@
505
532
  });
506
533
  }
507
534
 
508
- function fillOpen(el, bv) {
535
+ // ---------- the floor desk (task 1004558) ----------
536
+ function icon(d, f, size) {
537
+ var svg = svgEl('svg');
538
+ svg.setAttribute('width', String(size)); svg.setAttribute('height', String(size)); svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('aria-hidden', 'true');
539
+ var a = svgEl('path'); a.setAttribute('d', d || '');
540
+ svg.appendChild(a);
541
+ if (f != null) { var b = svgEl('path'); b.setAttribute('d', f || ''); svg.appendChild(b); }
542
+ return svg;
543
+ }
544
+ function chipBtn(cls, text, onClick) {
545
+ var b = node('button', 'desk-chip ' + cls, text);
546
+ b.setAttribute('type', 'button');
547
+ if (desk.busy) b.setAttribute('disabled', '');
548
+ b.addEventListener('click', onClick);
549
+ return b;
550
+ }
551
+ function fillDesk(el, bv) {
509
552
  clear(el);
553
+ var D = window.GovDesk, st = view.inside;
554
+ if (!D || !st) return;
555
+ var k = st.key + ':' + bv.selFloor;
556
+ var dm = D.deskModel({ key: st.key, floor: bv.selFloor, docket: data.docket, goals: (data.goals || []).filter(function (g) { return g && g.status === 'open'; }), now: Date.now(), room: data.room, constitution: data.constitution, done: desk.done, mine: desk.mine[k] || [], cleared: !!desk.cleared[k], today: today.n, sym: bv.sym });
557
+ desk.model = dm;
558
+ el.className = 'inside-open desk' + (dm.quiet ? ' desk--quiet' : '');
559
+ el.setAttribute('data-desk', dm.key);
560
+ // the header: the floor's sign, its name, its status
510
561
  var head = node('div', 'inside-open__head');
511
562
  var plate = node('span', 'inside-plate');
512
- var svg = svgEl('svg');
513
- svg.setAttribute('width', '40'); svg.setAttribute('height', '40'); svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('aria-hidden', 'true');
514
- var a = svgEl('path'); a.setAttribute('d', bv.sym.d || '');
515
- var b = svgEl('path'); b.setAttribute('d', bv.sym.f || '');
516
- svg.appendChild(a); svg.appendChild(b);
517
- plate.appendChild(svg);
563
+ plate.appendChild(icon(bv.sym.d, bv.sym.f, 34));
518
564
  head.appendChild(plate);
519
565
  var words = node('div', 'inside-open__words');
520
- words.appendChild(node('span', 'inside-open__kicker', bv.openKicker));
521
- words.appendChild(node('h3', 'inside-open__title', bv.openTitle));
566
+ words.appendChild(node('span', 'inside-open__kicker', 'floor ' + (bv.selFloor + 1)));
567
+ words.appendChild(node('h3', 'inside-open__title', dm.name));
568
+ words.appendChild(node('span', 'desk-status' + (dm.hot ? ' desk-status--hot' : ''), dm.status));
522
569
  head.appendChild(words);
523
570
  el.appendChild(head);
524
- bv.openLines.forEach(function (l) {
525
- var row = node('div', 'inside-line');
526
- row.appendChild(node('span', '', l.text));
527
- row.appendChild(node('span', 'inside-line__meta', l.meta));
528
- el.appendChild(row);
571
+ // up to three tiles, none of them a zero
572
+ if (dm.tiles.length) {
573
+ var tiles = node('div', 'desk-tiles');
574
+ dm.tiles.forEach(function (t) {
575
+ var tl = node('div', 'desk-tile');
576
+ tl.appendChild(node('span', 'desk-tile__v', t.v));
577
+ tl.appendChild(node('span', 'desk-tile__l', t.l));
578
+ tiles.appendChild(tl);
579
+ });
580
+ el.appendChild(tiles);
581
+ }
582
+ if (dm.quiet) {
583
+ // one line, lit: never an empty list
584
+ var q = node('div', 'desk-quiet' + (dm.cleared ? ' desk-quiet--cleared' : ''));
585
+ var tick = icon('M7.5 12.2l3 3 6-6.5', null, 44);
586
+ tick.setAttribute('class', 'desk-quiet__tick');
587
+ var ring = svgEl('circle'); ring.setAttribute('cx', '12'); ring.setAttribute('cy', '12'); ring.setAttribute('r', '9.5');
588
+ tick.appendChild(ring);
589
+ q.appendChild(tick);
590
+ q.appendChild(node('span', 'desk-quiet__line', dm.clearLine));
591
+ q.appendChild(node('span', 'desk-quiet__sub', dm.clearSub));
592
+ el.appendChild(q);
593
+ } else {
594
+ var list = node('div', 'desk-rows');
595
+ dm.rows.forEach(function (r) { list.appendChild(deskRow(r)); });
596
+ el.appendChild(list);
597
+ }
598
+ // the record line: what you just did here, then what this building closed
599
+ var rec = node('div', 'desk-record');
600
+ rec.appendChild(node('span', 'desk-record__label', 'record'));
601
+ if (!dm.record.length) rec.appendChild(node('span', 'desk-record__item', 'nothing yet'));
602
+ dm.record.forEach(function (x, i) { rec.appendChild(node('span', 'desk-record__item' + (x.mine ? ' desk-record__item--mine' : '') + (x.mine && i === 0 ? ' desk-pop' : ''), x.text)); });
603
+ el.appendChild(rec);
604
+ }
605
+ function deskRow(r) {
606
+ var row = node('div', 'desk-row' + (r.needs ? ' desk-row--needs' : ''));
607
+ row.setAttribute('data-row', r.key);
608
+ var main = node('div', 'desk-row__main');
609
+ main.appendChild(node('span', 'desk-ini', r.ini));
610
+ var name = node('button', 'desk-name');
611
+ name.setAttribute('type', 'button');
612
+ name.setAttribute('aria-expanded', desk.open === r.key ? 'true' : 'false');
613
+ name.setAttribute('data-matter', r.matter);
614
+ name.appendChild(node('span', 'desk-name__t', r.name));
615
+ name.appendChild(node('span', 'desk-name__f', r.fact));
616
+ name.addEventListener('click', function () { desk.open = desk.open === r.key ? null : r.key; desk.ask = null; draw(); focusRow(r.key, 'name'); });
617
+ main.appendChild(name);
618
+ var acts = node('span', 'desk-acts');
619
+ if (r.wait) acts.appendChild(node('span', 'desk-wait', r.wait));
620
+ // the primary button sits rightmost, as on the board
621
+ r.actions.slice().reverse().forEach(function (a) {
622
+ acts.appendChild(chipBtn('desk-chip--' + a.tone, a.label, function () {
623
+ if (a.go) { window.location.href = a.go; return; }
624
+ if (a.ask) { var at = r.key + '|' + a.key; desk.ask = desk.ask === at ? null : at; desk.open = null; draw(); focusRow(r.key, 'chip'); return; }
625
+ deskAct(r, a.key, {});
626
+ }));
529
627
  });
628
+ main.appendChild(acts);
629
+ row.appendChild(main);
630
+ if (desk.open === r.key) {
631
+ var facts = node('div', 'desk-facts');
632
+ r.facts.forEach(function (f) { facts.appendChild(node('span', 'desk-fact', f)); });
633
+ row.appendChild(facts);
634
+ }
635
+ var asked = desk.ask && desk.ask.indexOf(r.key + '|') === 0 ? (r.choices || {})[desk.ask.slice(r.key.length + 1)] : null;
636
+ if (asked) {
637
+ var chips = node('div', 'desk-choices');
638
+ chips.appendChild(node('span', 'desk-choices__ask', asked.ask));
639
+ asked.options.forEach(function (c) { chips.appendChild(chipBtn('desk-chip--choice', c.label, function () { deskAct(r, asked.for, c.input); })); });
640
+ if (asked.field) {
641
+ // the one typed value a desk takes: a name to invite (never a reason)
642
+ var input = node('input', 'desk-field');
643
+ input.setAttribute('type', 'text');
644
+ input.setAttribute('aria-label', asked.ask);
645
+ input.setAttribute('autocomplete', 'off');
646
+ input.setAttribute('maxlength', '60');
647
+ var send = function () { var v = input.value.trim(); if (v) deskAct(r, asked.for, { option: v, label: v }); };
648
+ input.addEventListener('keydown', function (e) { if (e.key === 'Enter') send(); });
649
+ chips.appendChild(input);
650
+ chips.appendChild(chipBtn('desk-chip--primary', 'send', send));
651
+ }
652
+ row.appendChild(chips);
653
+ }
654
+ return row;
655
+ }
656
+ function deskRowsEl() {
657
+ var st = view.insideModel;
658
+ var on = $(st && st.flipA ? 'insideOpenA' : 'insideOpenB');
659
+ var kids = on ? on.children : [];
660
+ for (var i = 0; i < kids.length; i++) if (/\bdesk-rows\b/.test(kids[i].className)) return kids[i];
661
+ return null;
662
+ }
663
+ function focusRow(key, what) {
664
+ var list = deskRowsEl();
665
+ var rows = list ? list.children : [];
666
+ for (var i = 0; i < rows.length; i++) {
667
+ if (rows[i].getAttribute('data-row') !== key) continue;
668
+ var main = rows[i].children[0];
669
+ var target = what === 'chip' && rows[i].children[1] ? rows[i].children[1].children[1] : main && main.children[1];
670
+ if (target && target.focus) target.focus();
671
+ return;
672
+ }
673
+ }
674
+ // The row clears away (a class the stylesheet animates), then the desk redraws without it.
675
+ function clearRow(key, then) {
676
+ var list = deskRowsEl();
677
+ var rows = list ? list.children : [];
678
+ for (var i = 0; i < rows.length; i++) if (rows[i].getAttribute('data-row') === key) rows[i].className += ' desk-row--gone';
679
+ if (typeof setTimeout !== 'function') return then();
680
+ setTimeout(then, 320);
681
+ }
682
+ var DONE_WORDS = {
683
+ for: 'voted yes', object: 'voted no', fine: 'let in', refuse: 'turned away', release: 'released', hold: 'held',
684
+ confirm: 'confirmed', dispute: 'disputed', fix: 'fixed', remove: 'removed', keep: 'kept', signoff: 'signed off', force: 'forced',
685
+ switch_on: 'switched on', switch_off: 'switched off', allow: 'allowed', disallow: 'disallowed', first: 'put first',
686
+ pause: 'paused', resume: 'resumed', tier: 'new tier', deactivate: 'deactivated', give: 'gave', take: 'took back',
687
+ check: 'checked', rot: 'set', module_on: 'turned on', module_off: 'turned off', backup: 'backed up', invite: 'invited',
688
+ };
689
+ // One act from a row: checked, sent through the card's own request (the
690
+ // server's permission checks decide), then the row clears, the floor counts
691
+ // it, the meter takes a pip and the toast says what happened. An act that
692
+ // records nothing (hold) clears the row for this visit and says it still waits.
693
+ function deskAct(r, key, input) {
694
+ var C = window.GovMatterCard, st = view.inside;
695
+ if (!C || !st || !r.card || desk.busy) return Promise.resolve(false);
696
+ var refused = C.check(r.card, key, input);
697
+ if (refused) { say(refused); return Promise.resolve(false); }
698
+ var k = st.key + ':' + (view.insideModel ? view.insideModel.selFloor : 0);
699
+ var req = C.request(r.card, key, input);
700
+ var settle = function (body) {
701
+ var a = C.answer(r.card, key, body);
702
+ desk.mine[k] = [DONE_WORDS[key] + ' ' + r.name].concat(desk.mine[k] || []);
703
+ desk.done[r.matter] = true;
704
+ desk.open = null; desk.ask = null;
705
+ if (desk.model && !desk.model.rows.some(function (x) { return x.matter !== r.matter; })) desk.cleared[k] = true;
706
+ if (st.floor == null && view.insideModel) st.floor = view.insideModel.selFloor;
707
+ bumpToday();
708
+ if (a.building && a.kind) card.answer = { building: a.building, matter: r.matter, kind: a.kind, at: Date.now() };
709
+ say(a.toast || 'Done.');
710
+ };
711
+ if (!req) return new Promise(function (res) { clearRow(r.key, function () { settle({}); draw(); res(true); }); });
712
+ desk.busy = true;
713
+ draw();
714
+ return send(req).then(function (out) {
715
+ desk.busy = false;
716
+ if (!out.ok) { say(C.refusal(out.status, out.body)); draw(); return false; }
717
+ return new Promise(function (res) {
718
+ clearRow(r.key, function () {
719
+ settle(out.body);
720
+ draw();
721
+ data.room = null; desk.roomAsked = false;
722
+ load().then(function () { res(true); });
723
+ });
724
+ });
725
+ });
726
+ }
727
+ // the today meter: decisions made today, kept for the day in this browser
728
+ function dayOf() { return new Date().toISOString().slice(0, 10); }
729
+ function readToday() {
730
+ var t = { day: dayOf(), n: 0 };
731
+ try {
732
+ var raw = window.localStorage ? JSON.parse(window.localStorage.getItem(TODAY_KEY) || 'null') : null;
733
+ if (raw && raw.day === t.day && isFinite(raw.n)) t.n = Math.max(0, Math.floor(raw.n));
734
+ } catch (e) { /* storage blocked: the meter starts at zero */ }
735
+ return t;
736
+ }
737
+ function bumpToday() {
738
+ if (today.day !== dayOf()) today = { day: dayOf(), n: 0 };
739
+ today.n += 1;
740
+ try { if (window.localStorage) window.localStorage.setItem(TODAY_KEY, JSON.stringify(today)); } catch (e) { /* the count still shows for this visit */ }
741
+ }
742
+ function drawToday() {
743
+ var D = window.GovDesk, pipsEl = $('streetTodayPips');
744
+ if (!D || !pipsEl) return;
745
+ var t = D.todayPips(today.n);
746
+ clear(pipsEl);
747
+ t.pips.forEach(function (p) { pipsEl.appendChild(node('span', 'street-today__pip' + (p.on ? ' street-today__pip--on' : '') + (p.fresh ? ' desk-pop' : ''))); });
748
+ setText('streetTodayN', t.text);
530
749
  }
531
750
 
532
751
  function pickFloor(i) {
@@ -534,6 +753,7 @@
534
753
  if (!st) return;
535
754
  st.floor = i;
536
755
  st.picks = (st.picks || 0) + 1;
756
+ desk.open = null; desk.ask = null;
537
757
  draw();
538
758
  var floors = $('insideFloors');
539
759
  var kids = (floors && floors.children) || [];
@@ -929,11 +1149,12 @@
929
1149
  if (win) win.addEventListener('scroll', placeCard);
930
1150
  if (media && media.addEventListener) media.addEventListener('change', function () { if (!storedTheme()) { applyTheme(); draw(); } });
931
1151
  painted = applyTheme();
1152
+ today = readToday();
932
1153
  draw();
933
1154
  watchShellTheme();
934
1155
  return load();
935
1156
  }
936
1157
 
937
- window.GovStreetPage = { boot: boot, draw: draw, load: load, fit: fit, openCard: openCard, closeCard: closeCard, actOn: actOn, pickFloor: pickFloor, goStreet: goStreet, amend: amend, fileDraft: fileDraft, FEEDS: FEEDS, THEME_KEY: THEME_KEY };
1158
+ window.GovStreetPage = { boot: boot, draw: draw, load: load, fit: fit, openCard: openCard, closeCard: closeCard, actOn: actOn, pickFloor: pickFloor, deskAct: deskAct, goStreet: goStreet, amend: amend, fileDraft: fileDraft, FEEDS: FEEDS, THEME_KEY: THEME_KEY };
938
1159
  window.GovStreetPage.ready = boot();
939
1160
  }());
@@ -55,14 +55,37 @@
55
55
  "auth": true,
56
56
  "_": "In the hall since task 1004544: the shell's full-screen switch pressed, the sidebar and the top bar gone, the street filling the window and the one exit in the corner (the sky's mechanism, task 1004327).",
57
57
  "actions": [
58
- ["wait", 900],
59
- ["click", "#fullscreen-toggle"],
60
- ["wait", 500]
58
+ [
59
+ "wait",
60
+ 900
61
+ ],
62
+ [
63
+ "click",
64
+ "#fullscreen-toggle"
65
+ ],
66
+ [
67
+ "wait",
68
+ 500
69
+ ]
61
70
  ],
62
71
  "expect": {
63
- "visible": ["#streetBoard", "#streetSigns", "#streetMode", "#fullscreen-toggle"],
64
- "hidden": ["#app-sidebar", "#topbar-title"],
65
- "attr": [["#fullscreen-toggle", "aria-pressed", "true"]]
72
+ "visible": [
73
+ "#streetBoard",
74
+ "#streetSigns",
75
+ "#streetMode",
76
+ "#fullscreen-toggle"
77
+ ],
78
+ "hidden": [
79
+ "#app-sidebar",
80
+ "#topbar-title"
81
+ ],
82
+ "attr": [
83
+ [
84
+ "#fullscreen-toggle",
85
+ "aria-pressed",
86
+ "true"
87
+ ]
88
+ ]
66
89
  }
67
90
  },
68
91
  "card": {
@@ -373,6 +396,291 @@
373
396
  "#insideLegend"
374
397
  ]
375
398
  }
399
+ },
400
+ "desk": {
401
+ "auth": true,
402
+ "_": "The floor desk (task 1004558): inside People the building opens on the floor that needs you, and the desk shows its sign, name and orange status, the tiles, the to-do rows with their two buttons, and the record line; the street header carries the today meter.",
403
+ "url": "/builders/street#building=people",
404
+ "actions": [
405
+ [
406
+ "wait",
407
+ 900
408
+ ],
409
+ [
410
+ "clickIfVisible",
411
+ "[data-craft-close]"
412
+ ],
413
+ [
414
+ "wait",
415
+ 300
416
+ ]
417
+ ],
418
+ "expect": {
419
+ "visible": [
420
+ ".desk-rows",
421
+ ".desk-status--hot",
422
+ ".desk-record",
423
+ "#streetTodayPips"
424
+ ]
425
+ }
426
+ },
427
+ "desk-facts": {
428
+ "auth": true,
429
+ "_": "A row name tapped: at most three facts open in place under it.",
430
+ "url": "/builders/street#building=people",
431
+ "actions": [
432
+ [
433
+ "wait",
434
+ 900
435
+ ],
436
+ [
437
+ "clickIfVisible",
438
+ "[data-craft-close]"
439
+ ],
440
+ [
441
+ "wait",
442
+ 300
443
+ ],
444
+ [
445
+ "click",
446
+ ".desk-name"
447
+ ],
448
+ [
449
+ "wait",
450
+ 500
451
+ ]
452
+ ],
453
+ "expect": {
454
+ "visible": [
455
+ ".desk-facts"
456
+ ]
457
+ }
458
+ },
459
+ "desk-quiet": {
460
+ "auth": true,
461
+ "_": "A quiet floor: one line with the lights on, never an empty list.",
462
+ "url": "/builders/street#building=review",
463
+ "actions": [
464
+ [
465
+ "wait",
466
+ 900
467
+ ],
468
+ [
469
+ "clickIfVisible",
470
+ "[data-craft-close]"
471
+ ],
472
+ [
473
+ "wait",
474
+ 300
475
+ ]
476
+ ],
477
+ "expect": {
478
+ "visible": [
479
+ ".desk-quiet"
480
+ ]
481
+ }
482
+ },
483
+ "desk-people-builders": {
484
+ "auth": true,
485
+ "_": "Task 1004559: People, Builders floor. Each builder is a row with tier and deactivate, the acts the old builders page held.",
486
+ "url": "/builders/street#building=people",
487
+ "actions": [
488
+ [
489
+ "wait",
490
+ 900
491
+ ],
492
+ [
493
+ "clickIfVisible",
494
+ "[data-craft-close]"
495
+ ],
496
+ [
497
+ "wait",
498
+ 300
499
+ ],
500
+ [
501
+ "click",
502
+ "[data-floor=\"2\"]"
503
+ ],
504
+ [
505
+ "wait",
506
+ 600
507
+ ]
508
+ ],
509
+ "expect": {
510
+ "visible": [
511
+ ".desk-rows",
512
+ ".desk-chip--primary"
513
+ ]
514
+ }
515
+ },
516
+ "desk-people-tier": {
517
+ "auth": true,
518
+ "_": "Task 1004559: the tier button opens its tiers as one-tap chips in place (one rung up at most).",
519
+ "url": "/builders/street#building=people",
520
+ "actions": [
521
+ [
522
+ "wait",
523
+ 900
524
+ ],
525
+ [
526
+ "clickIfVisible",
527
+ "[data-craft-close]"
528
+ ],
529
+ [
530
+ "wait",
531
+ 300
532
+ ],
533
+ [
534
+ "click",
535
+ "[data-floor=\"2\"]"
536
+ ],
537
+ [
538
+ "wait",
539
+ 600
540
+ ],
541
+ [
542
+ "click",
543
+ ".desk-row .desk-chip--primary"
544
+ ],
545
+ [
546
+ "wait",
547
+ 400
548
+ ]
549
+ ],
550
+ "expect": {
551
+ "visible": [
552
+ ".desk-choices"
553
+ ]
554
+ }
555
+ },
556
+ "desk-disputes": {
557
+ "auth": true,
558
+ "_": "Task 1004559: Disputes, Security reports. Confirm or dispute an open report; mark a confirmed one fixed.",
559
+ "url": "/builders/street#building=disputes",
560
+ "actions": [
561
+ [
562
+ "wait",
563
+ 900
564
+ ],
565
+ [
566
+ "clickIfVisible",
567
+ "[data-craft-close]"
568
+ ],
569
+ [
570
+ "wait",
571
+ 300
572
+ ],
573
+ [
574
+ "click",
575
+ "[data-floor=\"0\"]"
576
+ ],
577
+ [
578
+ "wait",
579
+ 600
580
+ ]
581
+ ],
582
+ "expect": {
583
+ "visible": [
584
+ ".desk-rows",
585
+ ".desk-status--hot"
586
+ ]
587
+ }
588
+ },
589
+ "desk-automation-goals": {
590
+ "auth": true,
591
+ "_": "Task 1004559: Automation, Allowed goals. Disallow, put first, or allow an open goal from chips.",
592
+ "url": "/builders/street#building=automation",
593
+ "actions": [
594
+ [
595
+ "wait",
596
+ 900
597
+ ],
598
+ [
599
+ "clickIfVisible",
600
+ "[data-craft-close]"
601
+ ],
602
+ [
603
+ "wait",
604
+ 300
605
+ ],
606
+ [
607
+ "click",
608
+ "[data-floor=\"1\"]"
609
+ ],
610
+ [
611
+ "wait",
612
+ 600
613
+ ]
614
+ ],
615
+ "expect": {
616
+ "visible": [
617
+ ".desk-rows"
618
+ ]
619
+ }
620
+ },
621
+ "desk-release-core": {
622
+ "auth": true,
623
+ "_": "Task 1004559: Release, Core update. Check first (a dry run); take it is offered only after the check passed for that version.",
624
+ "url": "/builders/street#building=release",
625
+ "actions": [
626
+ [
627
+ "wait",
628
+ 900
629
+ ],
630
+ [
631
+ "clickIfVisible",
632
+ "[data-craft-close]"
633
+ ],
634
+ [
635
+ "wait",
636
+ 300
637
+ ],
638
+ [
639
+ "click",
640
+ "[data-floor=\"3\"]"
641
+ ],
642
+ [
643
+ "wait",
644
+ 600
645
+ ]
646
+ ],
647
+ "expect": {
648
+ "visible": [
649
+ ".desk-rows"
650
+ ]
651
+ }
652
+ },
653
+ "desk-rules": {
654
+ "auth": true,
655
+ "_": "Task 1004559: Rules in force. The rot timer as day chips, work categories as a link, each module on or off.",
656
+ "url": "/builders/street#building=rules",
657
+ "actions": [
658
+ [
659
+ "wait",
660
+ 900
661
+ ],
662
+ [
663
+ "clickIfVisible",
664
+ "[data-craft-close]"
665
+ ],
666
+ [
667
+ "wait",
668
+ 300
669
+ ],
670
+ [
671
+ "click",
672
+ "[data-floor=\"0\"]"
673
+ ],
674
+ [
675
+ "wait",
676
+ 600
677
+ ]
678
+ ],
679
+ "expect": {
680
+ "visible": [
681
+ ".desk-rows"
682
+ ]
683
+ }
376
684
  }
377
685
  },
378
686
  "reducedMotion": {