This fixes a bug where if you re-clicked the current path you were on in
the configure sub-menu, it would unselect the item, even though if
that's where you were still at.
The reason is that the onClick handler was pre-populated with the text
`configure`. (However, the children were calling onClick with their own
paths). I'm not really sure why this even worked in the first place.
Anyway, now the types have been changed and I've removed the application
of the "configure" string as the active item.
First click (same both before and after):
<img width="285" height="181" alt="image"
src="https://github.com/user-attachments/assets/c13142ef-8ef8-4a9d-8719-68dcb16db7f0"
/>
Before (second click):
<img width="303" height="220" alt="image"
src="https://github.com/user-attachments/assets/6b576a91-2f1e-48f5-b8fe-9b1364c8987e"
/>
After (second click):
<img width="286" height="190" alt="image"
src="https://github.com/user-attachments/assets/72434401-414c-43e4-b5bf-7c972bb99b7a"
/>
Fixes a bug where the sidebar accordion would overflow if one of the
children had a badge. This was visible primarily in pro instances up
until now, but will also be an issue with the new "new" badge.
Instead of basing the width and min-width on the "mode", we now set it
to `auto` regardless.
This means that:
- When there's no badges, the sidebar will be slightly narrower than it
is today, because it doesn't need all that space.
- When there *are* badges, it will be slightly wider.
The previous change to these width values was in
https://github.com/Unleash/unleash/pull/8831 and was apparently to make
it "easier to use on medium screen sizes".
While this change makes it slightly wider when there are badges, I think
that's a worthwhile tradeoff to:
- not having the accordion body be wider than the rest of the menu
- not having badges have to be smooshed up against the text
There's no max width set, so technically you could force it super wide
by having very long menu items, but I think that's an acceptable
tradeoff that we can make. I also don't think it's very likely that
we'll run into that issue and not notice.
Further, on narrow screens, the sidebar is replaced by a modal anyway,
so it's not likely to be much of an issue, I think.
## Screenies
To show the changes, here are some screenies with widths:
### Before
The width is set to 256:
<img width="1270" height="1576" alt="image"
src="https://github.com/user-attachments/assets/5c8f1d72-9f23-4ec9-8278-b2fe529680d4"
/>
But notice how the configure menu pops out when we've badges:
<img width="580" height="1538" alt="image"
src="https://github.com/user-attachments/assets/9ebf4ff9-c11a-4716-a77f-0443d73dda90"
/>
This is the menu without badges. Still 256 px wide
<img width="554" height="1566" alt="image"
src="https://github.com/user-attachments/assets/0a8ad895-3409-494e-9f32-644938545707"
/>
### After
The width is slightly narrower when there's no badges:
<img width="1280" height="1654" alt="image"
src="https://github.com/user-attachments/assets/fdb792b6-c7be-408d-9e3b-a00c62794318"
/>
But slightly wider when there are badges to accommodate the extra
elements.
<img width="1292" height="1552" alt="image"
src="https://github.com/user-attachments/assets/e7437fa3-d747-44dc-9b0c-60e362c52833"
/>
For some reason, there's two nested buttons here, where only the outer
one is interactable. It doesn't matter much for mouse interactions, but
for keyboard interactions, they're both focusable (which is strange) and
the inner one doesn't do anything.
As such, this PR:
- removes it from the navigation tree (tabindex -1)
- changes the component to be a span instead of a button.
It keeps the role=undefined to override the default role="button" that
MUI sets on it.
The double button was also visible when the inner one had focus (the
overflow in the image is a different issue, though):
<img width="542" height="182" alt="image"
src="https://github.com/user-attachments/assets/2942033d-09ee-4806-a2de-a5e6a6680a35"
/>
https://linear.app/unleash/issue/2-4011/show-enterprise-edge-instance-stats-in-auth-app
Includes the current month, so far, in the average Edge instance usage
data.
E.g. my local dev instance stats look like this, since I rarely connect
an Edge instance to it:
```
{
"2025-11": 0.007,
"2025-10": 0.002,
"2025-09": 0,
"2025-08": 0,
"2025-07": 0,
"2025-06": 0,
"2025-05": 0,
"2025-04": 0,
"2025-03": 0,
"2025-02": 0,
"2025-01": 0,
"2024-12": 0
}
```
## About the changes
This adds handling of unhandled rejected promises at the process level,
so nodejs doesn't panic.
Also, adds tests around processing metrics and a refactor from previous
pr: #11010
Allows the `checkLatestVersion` function in the `VersionService` to
accept an optional `instanceInfo` parameter. If provided, and if the
promise returns a value that is truthy, then it will add `instanceInfo`
to the versionPayload.
The license key may not contain a plan or a customer name, and while it
definitely won't contain a client id, it has been requested that we
report `self-hosted` as the client ID (will be handled in enterprise).
Adding a second, optional parameter seemed to be the most backwards
compatible way of doing this rather than changing the established method
/ callback types.
## About the changes
Properly awaits all submitted promises, preventing the node's main
process from seeing rejected & unawaited promises.
What's going on?
- The bulk metrics handler pushes `registerBackendClient` promises into
promises.
- The next step (`clientMetricsEnvBulkSchema.validateAsync`) throws for
invalid metrics (e.g., `appName: null`), so we jump to catch and return
400.
- Because the code never reaches `Promise.all(...)`, the previously
spawned promises are never awaited. Node later detects the rejected
`registerBackendClient` promise as an **unhandled rejection** and
crashes the process. If that promise hadn’t been rejected, there’d be no
crash, but with invalid input, it does reject.
- **Fix:** always await the spawned tasks (using `Promise.allSettled`)
so every rejection is observed, even when validation later throws.
The `create` and `update` role methods used to blindly accept any
incoming permissions, but if the permissions don't exist in the
database, then the database would throw, yielding a 500 error to the
user.
To fix this, we can validate that all the permissions exist before we
try to add the incoming permissions.
The http error only manifests in enterprise, but the fix requires
modifying the access service. Therefore, I've added the tests to the
access service too, such that if you break something, then you don't
need to wait for it to propagate to enterprise.
---------
Co-authored-by: Gastón Fournier <gaston@getunleash.io>