This commit implements a three-tier sidebar with subcategories.
The first thing is that we've refactored the Sidebar into a holistic entity; rather than be built in
pieces, it's constructed declaratively from a tree of entries, much in the same way routes are, and
for much the same reason<sup>1</sup>.
The AdminSidebar element only provides the list of entries to show and some of the controls
necessary to show/hide the sidebar. Because the sidebar requires a rich collection of objects
retrieved from the back-end, to avoid cluttering the AdminSidebar each of those sublists of
TypeCreate have been isolated into their own controllers.
The SidebarTypeController isn't even a strongly reactive controller; all it does is fetch the
TypeCreate collection and notify the client object that the fetch has completed. The client can then
call the `.entries()` method on the controller to get the sub-tree of entries for the TypeCreate
object.
The Sidebar has been slightly (!) refactored so that it's emphatic about what it does: it shows the
brand, nav, and user sections of the sidebar. The styling has been moved to a separate file, the
better to emphasize this.
The SidebarItems file is where all the magic-- and a lot of ugly-- is hidden.
The main purpose of the SidebarItems is to render the tree of entries passed to it. That's it. But
it also has to be able to read the URL and highlight which entry is currently being shown by the
router, and it has to be able to open up all the parent objects of the "current" link so that the
user gets a clear sense of where they are navigationally.
Most messy: the `reclick()` function intercepts clicks on anchors and, using the same logic as the
router, decides if the router would *not* handle the navigation event. If the router would not, it
takes on the responsibility for reaching into the currently visible table, modifying the filter and
triggering a new `.fetch()`.
Somewhere along the way I boyscoutted another `switch` statement or two into lookup expressions.
---
<sup>1</sup> One of the reasons for this is that the Administrator Application's sidebar,
routes, and command palette will all get their data from a single source of truth, and that single
source will be independent of any of those. This is a step in that direction.
This commit enables the search function to work when changing from one tier 3 link to another in
the tier 2 category (Providers, Events, Stages, etc.). Because the router ignores such changes,
we must bring those changes to the attention of the receiver by hand.
This commit makes the recepient Table object locatable with a data attribute tag and a painstakingly
researched path to its most common location in our code.
It then checks any `anchor:click` event from the Sidebar to see if its likely to be elided
by the router. If it is, we take over, identify the Table object, set or clear its search
state to the desired state, and call the `.fetch()` method on the Table to initiate a new
search with that state.
This is, frankly, _gross_. The URL and internal states mirror the actual desired state more
or less by accident. But it's what we've got to work with at the moment.
* main: (47 commits)
web: bump the wdio group in /tests/wdio with 2 updates (#7702)
events: fix lint (#7700)
events: add better fallback for sanitize_item to ensure everything can be saved as JSON (#7694)
web: bump the wdio group in /tests/wdio with 4 updates (#7696)
events: include user agent in events (#7693)
web: fix labels on group view page (#7677)
website/docs: Add OIDC auth integration with Nextcloud (#7406)
web: fix locale (#7689)
core: bump python from 3.11.5-bookworm to 3.12.0-bookworm (#7048)
translate: Updates for file web/xliff/en.xlf in zh_TW (#7688)
web: bump pyright from 1.1.336 to 1.1.337 in /web (#7681)
core: bump sentry-sdk from 1.35.0 to 1.36.0 (#7683)
website: bump prism-react-renderer from 2.2.0 to 2.3.0 in /website (#7685)
web: bump the sentry group in /web with 2 updates (#7679)
web: bump rollup from 4.5.0 to 4.5.1 in /web (#7680)
web: bump @types/codemirror from 5.60.14 to 5.60.15 in /web (#7682)
web: bump the wdio group in /tests/wdio with 2 updates (#7684)
website: bump react-tooltip from 5.23.0 to 5.24.0 in /website (#7686)
core: bump goauthentik.io/api/v3 from 3.2023103.4 to 3.2023104.1 (#7687)
website/blog: Blog on security (#7671)
...
* web: break circular dependency between AKElement & Interface.
This commit changes the way the root node of the web application shell is
discovered by child components, such that the base class shared by both
no longer results in a circular dependency between the two models.
I've run this in isolation and have seen no failures of discovery; the identity
token exists as soon as the Interface is constructed and is found by every item
on the page.
* web: fix broken typescript references
This built... and then it didn't? Anyway, the current fix is to
provide type information the AkInterface for the data that consumers
require.
* web: rollback dependabot's upgrade of context
The most frustrating part of this is that I RAN THIS, dammit, with the updated
context and the current Wizard, and it finished the End-to-End tests without
complaint.
* web: fix labels on group view page
This is a wild bug, because what caused it and how it manifested are seemingly
unrelated as to be hallcinatory.
* web: break circular dependency between AKElement & Interface.
This commit changes the way the root node of the web application shell is
discovered by child components, such that the base class shared by both
no longer results in a circular dependency between the two models.
I've run this in isolation and have seen no failures of discovery; the identity
token exists as soon as the Interface is constructed and is found by every item
on the page.
* web: fix broken typescript references
This built... and then it didn't? Anyway, the current fix is to
provide type information the AkInterface for the data that consumers
require.
* A quality of life thing: `<ak-status-label good>`
There's an idiom throughout the UI:
``` HTML
<ak-label color=${item.enabled ? PFColor.Green : PFColor.Red}>
${item.enabled ? msg("Yes") : msg("No")}
</ak-label>
```
There are two problems with this.
- Repeating the conditional multiple times is error-prone
- The color scheme doesn't communicate much.
There are uses for ak-label that aren't like this, but I'm focusing on this particular use case,
which occurs about 20 times throughout the UI.
Since it's so common, let's isolate the most common case: `<ak-status-label good />` gives you the
"good" status, and `<ak-status-label/>` gives you the "bad" status, which is the default (no
arguments to the function).
There wasn't much clarity in the system for when to use orange vs red vs grey, but looking through
the use cases, it became clear that Red meant fail/inaccessible, Orange meant "Warning, but not
blocking," and Grey just means "info: this thing is off".
So let's define that with meaning: there are three types, error, warning, and info. Which
corresponds to debugging levels, but whatever, nerds grok that stuff.
So that example at the top becomes
```<ak-status-label ?good=${item.enabled}></ak-status-label>```
... and we can now more clearly understand what that conveys.
There is some heavy tension in this case: this is an easier and quicker-to-write solution to
informing the user of a binary status in an iconic way, but the developer has to remember that it
exists.
Story provided, and changes to the existing uses of the existing idiom provided.
* Added the 'compact label' story to storybook.
* web: break circular dependency between AKElement & Interface.
This commit changes the way the root node of the web application shell is
discovered by child components, such that the base class shared by both
no longer results in a circular dependency between the two models.
I've run this in isolation and have seen no failures of discovery; the identity
token exists as soon as the Interface is constructed and is found by every item
on the page.
* web: fix broken typescript references
This built... and then it didn't? Anyway, the current fix is to
provide type information the AkInterface for the data that consumers
require.
* web: rollback dependabot's upgrade of context
The most frustrating part of this is that I RAN THIS, dammit, with the updated
context and the current Wizard, and it finished the End-to-End tests without
complaint.
* Due for amendment
* Revert "Due for amendment"
This reverts commit 829ad5d3f2.
* web: refactor sidebar capabilities for categorical subsections
The project "Change Admin UI lists to have sublists per type" requires some initial changes to the
UI to facilitate this request. The AdminSidebar is the principle target of this project, and it is
embedded in the AdminInterface. To facilitate editing the AdminSidebar as an independent entity,
AdminInterface has been moved into its own folder and the AdminSidebar extracted as a standalone Web
Component. This removes, oh, about half the code from AdminInterface. A little cleanup with
`classMap` was also committed.
The rollup config was adjusted to find the new AdminInterface location.
The Sidebar uses the global `config: Config` object to check for Enterprise capabilities. Rather
than plumb all the way down through the Interface => AdminInterface -> AdminSidebar, I chose to make
provide an alternative way of reaching the `config` object, as a *context*. Other configuration
objects (Me, UiConfig, Tenant) interfaces will be contextualized as demand warrants.
Demand will warrant. Just not yet. <sup>1</sup>
The Sidebar has been refactored only slightly; the renderers are entirely the same as they were
prior to extraction. What has been changed is the source of information: when we retrieve the
current version we story *only* the information, and use type information to ensure that the version
we store is the version we care about. The same is true of `impersonation`; we care only about the
name of the person being impersonated being present, so we don't store anything else.
Fetches have been moved from `firstUpdated` to the constructor. No reason to have the sidebar
render twice if the network returns before the render is scheduled.
Because the path used to identify the user being impersonated has changed, the `str()` references in
the XLIFF files had to be adjusted. **This change is to a variable only and does not require
translation.**
---
<sup>1</sup> The code is littered with checks to `me()?`, `uiConfig?`, `config?`, etc. In the
*context* of being logged in as an administrator those should never be in doubt. I intend to make
our interfaces not have any doubt.
* Function to help generate sizing solutions across Javascript and CSS.
* web: refactor sidebar capabilities for categorical subsections
Move open/close logic into the ak-admin-sidebar itself.
This commit removes the responsibility for opening/closing the sidebar from the interface parent
code and places it inside the sidebar entirely. Since the Django invocation passes none of the
properties ak-interface-admin is capable of receiving, this seems like a safe operation.
The sidebar now assumes the responsibility for hooking up the window event listeners for open/close
and resize.
On connection to the DOM, and on resize, the sidebar checks to see if the viewport width meets the
criteria for a behavioral change (slide-overlay vs slide-push), and on slide-push automatically
opens the sidebar on the assumption that there's plenty of room. In order to support more dynamic
styling going forward, I've substituted the 1280px with 80rem, which is the same, but allows for
some better styling if someone with older eyes needs to "zoom in" on the whole thing with a larger
font size.
The hide/show code involves "reaching up" to touch the host's classList. There's a comment
indicating that this is a slightly fragile thing to do, but in a well-known way.
* dev: (21 commits)
sources/ldap: clean-up certs written from db (#7617)
web: bump the eslint group in /tests/wdio with 1 update (#7635)
core: compile backend translations (#7637)
core: bump psycopg from 3.1.12 to 3.1.13 (#7625)
core: bump ruff from 0.1.5 to 0.1.6 (#7626)
core: bump twilio from 8.10.1 to 8.10.2 (#7627)
web: bump the eslint group in /web with 1 update (#7629)
web: bump the esbuild group in /web with 2 updates (#7630)
web: bump rollup from 4.4.1 to 4.5.0 in /web (#7631)
web: bump core-js from 3.33.2 to 3.33.3 in /web (#7633)
core: bump goauthentik.io/api/v3 from 3.2023103.3 to 3.2023103.4 (#7634)
web: bump the wdio group in /tests/wdio with 4 updates (#7636)
translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_TW (#7628)
root: specify node and python versions in respective config files, deduplicate in CI (#7620)
translate: Updates for file web/xliff/en.xlf in zh-Hans (#7619)
translate: Updates for file web/xliff/en.xlf in zh_CN (#7618)
tests: better per-test timeouts (#7612)
web: bump API Client version (#7613)
stages/identification: add option to pretend user exists (#7610)
events: stop spam (#7611)
...
This commit restores the onHashChange functionality, using an
on-demand reverse map (there really aren't that many objects in the
nav tree) to make sure all of the parent entities are also listed
in the "expanded" listing to make sure the target object is still
visible. Along the way, several type lever errors were corrected.
Two major pieces of functionality were extracted from the Sidebar
function as they're mostly consumers/filters of the information
provided, and don't need to be in the Sidebar itself.
of the TypeCreate objects and replaces them with a single generic ReactiveController, which it
then instantiates six times, but at least they're shorter!
* main: (42 commits)
stages/authenticator_totp: fix API validation error due to choices (#7608)
website: fix pricing page inconsistency (#7607)
web: bump API Client version (#7602)
translate: Updates for file web/xliff/en.xlf in zh_CN (#7603)
core: bump goauthentik.io/api/v3 from 3.2023103.2 to 3.2023103.3 (#7606)
translate: Updates for file web/xliff/en.xlf in zh-Hans (#7604)
Revert "web: bump @lit-labs/context from 0.4.1 to 0.5.1 in /web (#7486)"
root: fix API schema for kotlin (#7601)
web: bump @lit-labs/context from 0.4.1 to 0.5.1 in /web (#7486)
translate: Updates for file web/xliff/en.xlf in zh-Hans (#7583)
events: fix missing model_* events when not directly authenticated (#7588)
translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_TW (#7594)
providers/scim: fix missing schemas attribute for User and Group (#7477)
core: bump pydantic from 2.5.0 to 2.5.1 (#7592)
web/admin: contextually add user to group when creating user from group page (#7586)
website/blog: title and slug change (#7585)
events: sanitize functions (#7587)
stages/email: use uuid for email confirmation token instead of username (#7581)
website/blog: Blog about zero trust and wireguard (#7567)
ci: translation-advice: avoid commenting after make i18n-extract
...
The actual behavior is more or less what I expected. What's missing is:
- Persistence of location (the hover effect fades with a click anywhere else)
- Proper testing of the oddities
- Full (or any!) responsiveness when moving between third-tier links in the same category
Stretch goal:
- Remembering the state of the sidebar when transitioning between the user and the admin (this will require using some localstorage, I suspect).
I also think that in my rush there's a bit of lost internal coherency. I'd like to figure out what's wiggling around my brain and solve that discomfort.
The third tier works! The only problem is that route isn't responsive, and I'm not sure why.
If you leave the `Providers` and go somewhere else, then click on a third-tier, the filter
works fine. But if you click on one third-tier and then another, the filter doesn't
change. Must investigate.
I've finally reached a stage where I have a framework I can build upon, but what
a pain in the posterior it was to get here. Keeping the entire navigation list
within a single DOM is a solid idea, but porting from the original code to this
proved to be unreasonably kludegy. Instead, I started from scratch, adding each
step along the way, a sort of Transformation Priority Premise, and testing each
step to make sure it was all behaving as expected.
So far, so good.
The remaining details are not trivial: I have to figure out how to express the
different classes of actions, and get the third tier working, but at least
the React version gives us hints.