Skip to content

Patch/5 instance hub - #1063

Open
Emissaryx wants to merge 77 commits into
dalen:mainfrom
Emissaryx:patch/5-instance-hub
Open

Patch/5 instance hub#1063
Emissaryx wants to merge 77 commits into
dalen:mainfrom
Emissaryx:patch/5-instance-hub

Conversation

@Emissaryx

Copy link
Copy Markdown

No description provided.

… stats

Share view links now bake in an absolute start/end timestamp instead of a relative range, so a copied link keeps showing the same window. Standouts and roster-preview column order now matches the full Scenario detail page. Expanded match rows in Scenario Breakdown show the score. Dropped the invented Overall contribution stat - Standouts now sort by real stats directly, and Top contributor is now Top killer with a tooltip. Added tooltips for Balance thresholds. Filter selects use Bulma's select component.
Averaging or taking the median over 1-2 scenarios lets a single lucky match dominate the ranking regardless of which stat is selected. Reproduced live: Destruction Standouts ranked by Kill damage, Average view, put a 1-scenario player at the top. Default the minimum-scenarios filter to 10+ whenever View is Average or Median; Totals keeps the old 1+ default. An explicit lbMin in the URL always overrides this.
Reworked from one flat sortable table into two team sections grouped by role (Tank/Melee DPS/Ranged DPS/Healer) with a team totals row, matching the roster preview pattern and the maartenson.net reference. Every stat cell also shows the player's share of their team total for that stat, e.g. Kill dmg 27K (11%), so healer/dps/tank contribution can be compared honestly without a combined score. Sorting still works, applied within each role group.
Emissaryx and others added 29 commits August 3, 2026 20:43
Refactor career filter layout to use a grid format and improve accessibility.
Refactor creature title functions and improve readability.
Refactor VendorItems component to load all vendor items in one go instead of paginating with server-side queries. Update search functionality and adjust item display logic.
Add a scrollable card for vendor items to keep the list in view.
Added error handling for malformed vendor items.
Instead of discarding an entire 50-item page when a single row
references deleted data (which nulls the whole array under GraphQL's
non-null propagation rules), binary-split the failing range and retry
each half until the bad row(s) are isolated. Recovers all valid items
in a page and only drops the specific malformed rows.
…ta-loss bugs

- SearchBox now filters/searches as you type (debounced ~300ms), matching
  the pattern used on the creature vendor-items filter, instead of
  requiring Enter/submit. Affects Creatures, Items, Quests, Instances,
  and both dedicated search pages.
- Fixed a real bug: SearchBox unconditionally navigated to
  /search/guild/:query on submit regardless of context, so pressing
  Enter in the Creatures/Items/Quests/Instances search box redirected
  users to Guild Search instead of filtering the current page. Only the
  two pages that actually own a /search route now navigate
  (navigateOnSubmit), and it replaces history instead of pushing so
  live typing doesn't spam back-button history.
- ScenarioList's full-window loader and CharacterScenarioConnections'
  deathblow loader had the same 'one malformed row nulls the whole
  page/request' vulnerability fixed earlier in VendorItems. Applied the
  same errorPolicy + range-splitting recovery to ScenarioList, and
  added a missing catch block to CharacterScenarioConnections so a
  request failure there no longer silently discards already-loaded
  data.
- Home.tsx's two homepage search boxes (player and guild leaderboard
  tabs) were completely broken: they never had an onSubmit handler,
  they relied entirely on SearchBox's old always-navigate default. Once
  navigation became opt-in (navigateOnSubmit) to fix the wrong-page bug,
  these two lost their only way to do anything at all. Added
  navigateOnSubmit to both.
- Fixed a feedback-loop bug in SearchBox: syncing local input state from
  initialQuery on every change meant each debounced commit's resulting
  URL update fed straight back into the input, which could clobber a
  keystroke (most visibly backspace) typed in the gap before that round
  trip resolved -- typing into the guild/player search boxes felt stuck.
  Now the sync only fires when initialQuery changes for a reason other
  than our own last commit (e.g. actual browser back/forward).
The if (loading) return <progress/> pattern unmounted the entire page
(including the SearchBox) on every debounced refetch, kicking focus
out of the input mid-word. SearchBox now stays mounted outside the
loading/error gate on Creatures, Items, Quests, Instances, Search,
and SearchGuild; only the results area conditionally shows the
spinner/error/empty state.
…d' in it

Clearing the box while navigateOnSubmit is set built `/search/guild/`
(empty trailing segment) and navigated there. React Router has no route
for that exact shape, but bare /search/guild does match the player route
/search/:query with query="guild" - so it landed back on a search box,
just the player one, pre-filled with the literal text "guild" instead of
going back to the guilds/players home page.

Now an empty value navigates straight to '/' (player) or '/guilds' (guild)
instead of building a URL with a dangling empty segment.
…tion

navigateOnSubmit boxes (Home's player/guild tabs, the Search and
SearchGuild pages) can jump between pages mid-search: a debounced commit
navigates from e.g. /guilds to /search/guild/:query, which is a different
route element - React unmounts the old input and mounts a brand-new one,
taking focus with it. The user had to click back into the box to keep
typing past whatever they'd typed when the debounce fired.

Focus (and restore the cursor to the end of the value) on mount for
navigateOnSubmit boxes. This only fires once per page visit: staying on
the same search results page for further keystrokes just updates the URL
param, it doesn't remount.
Inspired by maartenson.net's per-dungeon pages (Bastion Stairs etc), which
show tabs for recent Runs, the Characters who've run it, and a
Leaderboards view. That site tracks its own historical data with
hourly graphs and a multi-day date-compare tool - replicating that would
need a scraper and years of storage, so this is a scoped-down first pass:
one GraphQL request for the most recent 50 runs of an instance (the API
already supports filtering instanceRuns by instanceId, with each run's
full scoreboard inline), and all three tabs are derived from that single
batch client-side. No new backend, cron, or cache.

- Runs: recent runs with duration/deaths, plus the API's own average
  duration/deaths for the batch.
- Characters: everyone who's run it recently, sorted by run count, with
  total damage/healing/protection/deaths.
- Leaderboards: best single-run performance per character for a chosen
  metric (damage/healing/protection), filterable by role and realm.

New route /instance/:id (+ /characters, /leaderboards). The Instances list
page's name link and 'Runs' button, which already pointed at /instance/:id
with no matching route, now land here instead of 404ing.
InstanceRunFilterInput.instanceId expects the custom UnsignedShort
scalar, not Int - declaring the query variable as $instanceIdNum: Int!
failed GraphQL's variable-type-compatibility check at query time
('CombinedGraphQLErrors: The variable instanceIdNum is not compatible
with the type of the current location'), so the Runs tab never loaded.
Declaring it as UnsignedShort! (matching InstanceRunsFilters.tsx's other
uses of this same field) fixes it.
The connection-level averageDuration field is a Duration scalar that
comes back as an ISO-8601 string ('P19DT16H26M27.5S'), not milliseconds -
new Date(Math.round(iso)) silently produced Invalid Date, so the stat
rendered blank. It's also an all-history average (skewed by a small
number of runs in the underlying data with bogus multi-day end times),
which didn't match the page's own 'most recent N runs' framing anyway.

Now both averageDuration and averageDeaths are computed client-side from
the same loaded batch shown in the runs table below, so the summary card
and the table always agree.
…ustom)

No new Worker/D1 needed - instanceRuns already supports start:{gte,lte}
filtering server-side, so this reuses the same windowed-loading trick as
ScenarioList's time-range picker: page through the live API in batches
for the selected window instead of the fixed most-recent-50 request.
@netlify

netlify Bot commented Aug 4, 2026

Copy link
Copy Markdown

Deploy Preview for curious-caramel-c7f0d8 ready!

Name Link
🔨 Latest commit 56173da
🔍 Latest deploy log https://app.netlify.com/projects/curious-caramel-c7f0d8/deploys/6a713770602d0000089ed7f5
😎 Deploy Preview https://deploy-preview-1063--curious-caramel-c7f0d8.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

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