Skip to content

refactor(apps): remove ~11,000 lines of dead and duplicated app code - #660

Merged
MrYuion merged 23 commits into
developfrom
refactor/apps-cleanup
Nov 2, 2026
Merged

MrYuion merged 23 commits into
developfrom
refactor/apps-cleanup

Conversation

@MrYuion

@MrYuion MrYuion commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The workplace, concierge, visitor kiosk, map kiosk, booking panel and control apps had built up code that nothing uses, many copies of the same logic, and a few imports of other apps' source. This continues #653, which did the same for the libs. The audit also found some small bugs.

Solution

One commit per block, so each can be reviewed alone. The branch removes about 16,400 lines and adds about 5,400.

  • Dead code. 37 unused files (components, pipes, specs) and about 65 unused members in files that stay. This includes the concierge reports menu, which never showed because /reports redirects to the desk report, and the unused virtual_concierge.prompts setting.
  • Bug fixes.
    • Locker check-out messages showed a raw translation key.
    • The lockers and visitors reports downloaded as report+assets….
    • Parking request success ignored the booking timezone.
    • The "no timezones" option could never show in the region, building and room modals.
    • ?clear=true on the visitor kiosk kept the stored location.
    • Survey removal left its dialog loading when the request failed.
    • Review fixes: the system setup screen could search before the organisation loaded. A kiosk level change kept the location from the old level. The visitor kiosk idle reset left dialogs open for the next visitor.
  • Shared lib code.
    • themedLogo() replaces about 20 copies of the dark/light logo choice. Logos now follow theme changes; several copies read a non-reactive getter.
    • AnnounceDirective and mapWithLimit are shared, and five apps use one ngsw-config.json.
    • One system picker setup screen serves booking-panel and control. One kiosk location picker serves map-kiosk and visitor-kiosk.
  • No more cross-app imports. Concierge imported workplace components, and visitor-kiosk imported the map-kiosk explore page. They are now in libs/bookings, libs/components and libs/explore. The visitor kiosk ran two idle timers on /explore; it now runs one.
  • Concierge.
    • The settings modals share their load and save code. <settings-select> replaces 63 copies of one select field, and one App Settings menu replaces four.
    • Eight report pages share one page shell. The desk and space reports share their charts and usage list.
    • More flows use confirmAction. The day-view timelines share their maths, <timezone-field> replaces four copies, and the zone helpers are shared.
  • Workplace. One calendar-links component serves five success pages. The schedule filters, the group-booking detection and restore, and the nav items each have one copy now. Desk confirm shows field labels instead of raw keys.

Behavior changes

  • Control setup screen. It uses the booking-panel layout: lighter background and a bordered card. The booking-panel setup screen looks the same, but it now reacts to query-param changes after load and does not navigate twice. On both, the search waits for the organisation to load.
  • Map kiosk setup. Labels are translated. Two prompts change wording, and the button now reads "Finish setup" on both kiosks.
  • Visitor kiosk setup. Finish needs a level. Before, it was enabled with only a building and then did nothing.
  • Concierge actions. URL, email template, event and visitor actions show the error and resolve on failure instead of rethrowing. Cancelling an event delete keeps the details modal open.

Not done

  • Merges that weren't simpler. The workplace parking and locker confirm/success pages, the concierge parking and lockers daily-usage components, and switching the day-view remove() to removeBooking (that would send an empty system id for some rooms).
  • Product decisions. The legacy flows flag, the visitor kiosk covid route, the map kiosk /desks page, and unused search methods in the zone and POI services.
  • Out of scope. outlook-addin still imports workplace source.

Verification

  • nx affected -t test,build passes for 30 projects after the rebase onto develop. Under parallel load, libs/explore once reported an uncaught window is not defined from explore-sensor-info.component.spec.ts after all 27 files passed. It passes on its own (3 of 3 runs), and this branch does not change that spec or its component.
  • oxlint and ESLint (templates) are clean.
  • New focused specs for the bugs. Each fails without its fix.

Reviewed with sol-loop: 12 rounds over 10 blocks with gpt-6.1-sol on xhigh. Sol verdict: approved

Changes made by Claude Opus 5.5 (claude-opus-5-5) in Claude Code, running in T3 Code. Claude subagents ran the first audit and wrote parts of the larger blocks. GPT-6.1 Sol (Codex) reviewed each block.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
frontend-templates Ignored Ignored Preview Nov 2, 2026 11:09pm UTC

@MrYuion MrYuion left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Claude Opus 5.5 responding on behalf of Alex Sorafumo

The removals look safe. I found no code that the PR deletes and that something still uses. I checked imports, routes, project.json service-worker paths and the removed settings. The five deleted ngsw-config.json files match shared/ngsw-config.json except for the $schema path.

Three inline comments. The first is a regression from the shared setup screen:

  1. SystemBootstrapComponent no longer waits for the organisation, so the booking-panel system search can run without zone_id.
  2. The shared kiosk location picker keeps a location from the previous level. This bug was already in both kiosks.
  3. The visitor kiosk idle reset is now the only reset on /explore, but it does not close dialogs.

One more small item: booking-calendar-links.component.ts copies the typo rel="noopener noreferer" to all three links. The typo was already there, and this PR puts it in one place, so it is a one-word fix now.

Verified at 130dd62c: nx affected -t build,test builds all affected apps, and all test suites pass except explore:test. That one failed once under parallel load with the uncaught window is not defined from explore-sensor-info.component.spec.ts, as the PR description says. It passed 2 of 2 runs on its own. oxlint, the ESLint template lint and e2e:typecheck are clean.

q: search,
limit: 20,
fields: ['id', 'name', 'display_name', 'email'].join(','),
zone_id: this._org.organisation?.id,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Claude Opus 5.5 responding on behalf of Alex Sorafumo

This search does not wait for OrganisationService, so booking-panel can query systems with zone_id: undefined. The old booking-panel bootstrap returned early with if (!this._org.initialised()) return; and ran again when the org loaded. This version uses control's old code, which did not wait.

If the user types 2 or more characters before the org loads, the request is not limited to the organisation. The resource also does not run again when the org becomes ready, because params only reads the search text.

To fix it, make the org part of params. When params returns undefined, the resource skips the load:

params: () =>
    this._org.initialised()
        ? { q: this._debounced_search.value(), zone_id: this._org.organisation?.id }
        : undefined,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Claude Opus 5.5 responding on behalf of Alex Sorafumo

Fixed in 7b19761. The resource params returns '' until this._org.initialised() is true, so the loader does not search. When the org loads, params changes and the search runs with zone_id set. I used '' in place of undefined because the loader already returns [] for short text. The spec should wait for the organisation before searching checks both steps, and it fails without the fix.

aria-labelledby="bootstrap-level-label"
#select
level
[(ngModel)]="active_level"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Claude Opus 5.5 responding on behalf of Alex Sorafumo

A level change does not clear active_location. This bug was in both old kiosk bootstraps, but the shared picker is now the one place to fix it.

To reproduce: pick level A and location L1, then change to level B. The location select shows nothing selected, or it does not show if B has no locations. submit() still sends location: L1, and saveKioskLocation writes a KIOSK.location that is not on the saved level.

To fix it, do the same as setRegion and setBuilding:

[ngModel]="active_level()"
(ngModelChange)="setLevel($event)"

Add a setLevel method that sets the level and clears active_location.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Claude Opus 5.5 responding on behalf of Alex Sorafumo

Fixed in 7b19761. The level select now uses [ngModel]="active_level()" with (ngModelChange)="setLevel($event)". setLevel sets the level and clears active_location, the same as setRegion and setBuilding. The spec should clear the location when the level changes fails without the fix.

*/
@Component({
selector: 'visitor-kiosk-explore',
template: `<div app-explore [reset_when_idle]="false"></div>`,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Claude Opus 5.5 responding on behalf of Alex Sorafumo

With [reset_when_idle]="false", returnToWelcomeWhenIdle is the only idle reset on /explore. It calls checkin.clear() and router.navigate(['/welcome']), but it does not close MatDialogs. The explore reset did that with this._dialog.closeAll().

closeOnNavigation does not help here. It reacts to Location popstate and hashchange events, and router.navigate uses pushState, also with withHashLocation(). So a space, desk or locker details dialog that a visitor leaves open on /explore stays over the welcome page for the next visitor.

This was mostly true before too: with the defaults, the app timer (120 s) fired before the explore timer (180 s). But the PR now makes the app reset the only reset, so it is the place to fix this. Inject MatDialog in inactivity-reset.ts and call closeAll() before you navigate.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Claude Opus 5.5 responding on behalf of Alex Sorafumo

Fixed in 7b19761. returnToWelcomeWhenIdle injects MatDialog and calls closeAll() before it clears the check-in and goes to /welcome. The idle reset spec now expects closeAll on timeout, and it fails without the fix.

Remove 37 files that no route, template, dialog or import uses in
workplace, concierge, map-kiosk, visitor-kiosk and control. Remove the
concierge reports menu, which never showed because /reports redirects to
the desk report, and the unused virtual_concierge.prompts setting.
Control uses the lib mediaDuration pipe in place of its own copy.
Remove methods, fields, injections and template blocks that nothing reads
in workplace, concierge, map-kiosk, visitor-kiosk and booking-panel, with
the spec cases that only tested them.
- Locker check-out messages use the correct translation keys.
- Lockers and visitors reports no longer download as report+assets.
- Parking request success shows times in the booking timezone.
- The "no timezones" option shows when the filter matches nothing.
- Visitor kiosk clears the stored location and stale orientation.
- Survey and question removal close the dialog when the request fails.
- themedLogo() replaces about 20 copies of the dark/light logo choice.
  The logos now follow theme changes.
- AnnounceDirective moves to libs/components. It waits for translations
  and does not announce after its element is destroyed.
- mapWithLimit moves to libs/common.
- Five apps share one ngsw-config.json.
SystemBootstrapComponent replaces the booking-panel and control setup
screens. KioskLocationPickerComponent replaces the five location selects
of the map-kiosk and visitor-kiosk setup screens. Control setup now uses
the booking-panel layout, and map-kiosk labels are translated.
- Desk and parking request form details move to libs/bookings.
- Fullscreen embed moves to libs/components.
- The map-kiosk explore page moves to libs/explore. Visitor kiosk turns
  off its idle reset, so only one idle timer runs on /explore.
The settings modals share their layered load and save. settings-select
replaces 63 copies of the same select field. One App Settings menu and
openAppSettings() replace the copies in the zone, building, region and
level lists.
Eight report pages use one page shell and one query helper. One charts
component and one usage list serve the desk and space reports. One
query call replaces five identical booking query branches.
- URL, email template, event and visitor actions use confirmAction.
- The day-view timelines share viewEvent and their time calculations.
- timezone-field replaces four timezone autocompletes.
- Shared zone scope, level selection and parking request permission.
The event details modal stays open when a removal fails.
- booking-calendar-links replaces the links block in five success pages.
- Schedule filter types, counts and feature checks are shared.
- isGroupBooking() and restoreGroupMembers() replace four copies.
- Top and footer menus use one list of nav items.
- Desk confirm shows field labels for invalid fields.
…set review issues

- The system setup screen waits for the organisation before it searches,
  so the search is scoped to the organisation zone.
- The kiosk location picker clears the location when the level changes.
- The visitor kiosk idle reset closes open dialogs before it goes to the
  welcome page.
- The calendar links use rel="noopener noreferrer".
@MrYuion
MrYuion force-pushed the refactor/apps-cleanup branch from 130dd62 to 7b19761 Compare November 2, 2026 21:01
…apps

- Signage: the animation time setting now reaches the CSS transition.
- Stagehand: failed loads clear the progress bar and show an error. The
  editors do not save over another dashboard or alert after a failed
  load, and leaving during a load does not redirect.
- Stagehand: missing translation keys and the post-save route are fixed.
- Assistant panel: the chat binds to the system chosen at setup.
- Chat: sending reopens a closed chat; the info toggle works.
- Enrolment: a failed check-in no longer leaves the spinner forever.
- Caterer UI: the landing tile images are in shared assets.
- App loader: the app grid layout is back.
- Redirect: the ellipsis animation is only on the redirect message.
…counts

- Map Studio: rotating a rotated object no longer jumps.
- Map Studio: floors traced from an image store the image size, so the
  kiosk places objects like the editor.
- Map Studio: systems load with paging, not only the first 100.
- Signage Manager: item counts use the plural form, and two picker
  modals show a load error with retry.
- Outlook add-in: remove the legacy booking flow. The manifest only
  opens the calendar pane. This also removes the imports of workplace
  source.
- Stagehand: remove time conditions (users could not add them), unused
  dashboard components and routes, and dead members.
- Map Studio: remove unused version history, CSV import/export, editor
  mode and other members.
- Signage Manager: remove unused translation keys and a dead fallback.
- Remove small dead members in signage, enrolment, survey, redirect,
  assistant panel and caterer UI.
The cron rules, schedule mask check, play-once parsing, media animation,
template layout and preview message names move to
libs/common/src/lib/signage. The signage manager now uses the player's
rules, so it no longer calls a schedule playable when the player will
not play it.
- Assistant panel uses the shared system setup screen. The shared screen
  waits for the organisation before it searches.
- 13 apps share one test setup.
- 8 more apps use shared/ngsw-config.json, which adds the S3 cache to
  enrolment, outlook add-in, survey and timetable.
- 13 apps reload on chunk load errors.
- Small config fixes in outlook add-in, survey, signage, caterer UI,
  app loader, enrolment and timetable.
Use themedLogo, withTimeout, errorMessage, mapWithLimit, parseJWT and
firstTruthyValueFrom from the libs. The catering availability and charge
code dialogs move to CateringStateService for caterer UI and concierge.
One tab bar with keyboard support, one section header, one assigned
playlists panel, one playlist status badge, one group members card and
one picker modal shell replace the copies. Status badges use one colour
set.
- Display and zone services share playlist assignment.
- Playlist undo has a confirm step, like template undo.
- One translated schedule summary replaces the English-only copy. Times
  follow the app's 24-hour setting.
- One confirm helper for removes shows progress and always closes.
- Change keys are counters, so two changes in one millisecond are not
  lost.
- Stagehand: one page layout, one URL filter handler, one nav list.
- Map Studio: one bookable check, bounds helper, image loader and
  validation on the objects in memory.
- Public: shared query helpers; registration and save failures no longer
  leave unhandled rejections.
If no chat system is set, the chat cannot connect. sendMessage now shows
an error and returns false, and the chat keeps the text in the input.
An error also shows if the connection closes before it opens.
@MrYuion

MrYuion commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator Author

Note

🤖 Claude Opus 5.5 responding on behalf of Alex Sorafumo

Fixed in 7b19761: all three links now use rel="noopener noreferrer". The same typo is in seven other files that this PR does not change. I left them out of this PR.

refactor(apps): remove ~8,300 lines from the remaining 13 apps and fix their bugs
Resolve two signage-manager conflicts:
- playlist-header keeps the shared section header and gains the Router
  that opens a new playlist.
- media-select-modal keeps the shared picker-modal frame and gains the
  multi-select, the heading from the dialog data and the confirm footer.
@MrYuion
MrYuion merged commit cd2dacf into develop Nov 2, 2026
3 checks passed
@MrYuion
MrYuion deleted the refactor/apps-cleanup branch November 2, 2026 23:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant