* web: the return of pseudolocalization
The move to lit-locale lost the ability to automagically pseudolocalize the UI, a useful
utility for checking that additions to the UI have been properly cataloged as
translation targets. This short script (barely 40 lines) digs deep into the lit-localize
toolkit and produces a pretranslated translation bundle in the target format folder.
* Linted, prettied, and commented.
Everywhere else, the label "Attributes" refers to the dictionary field that our
core models have and use as a JSON repository for key/value pairs that don't need
the full model's awareness or management. Using the same label here, where the
term refers to the context data of the invitation itself, may confuse people,
so we've changed the label to signal its difference.
* web: patternfly hints as ak-web-component
Patternfly 5's "Hints" React Component, but ported to web components.
The discovery that CSS Custom Properties are still available in
child components, even if they're within independent ShadowDOMs,
made this fairly easy to port from Handlebars to Lit-HTML. Moving
the definitions into `:host` and the applications into the root DIV
of the component made duplicating the Patternfly 5 structure
straightforward.
Despite the [Patternfly
Elements]documentation](https://patternflyelements.org/docs/develop/create/),
there's a lot to Patternfly Elements that isn't well documented,
such as their slot controller, which near as I can tell just makes
it easy to determine if a slot with the given name is actually being
used by the client code, but it's hard to tell why, other than that it
provides an easy way to determine if some CSS should be included.
* Pre-commit fixes.
* web: fix some issues with styling found while testing.
* web: separated the "with Title" and "without Title" stories.
* Added footer story, fixed some CSS.
* web: hint controller
Add the `ShowHintController`. This ReactiveController takes a token
in its constructor, and looks in LocalStorage for that token and
an associated value. If that value is not `undefined`, it sets the
field `this.host.showHint` to the value found.
It also provides a `render()` method that provides an `ak-hint-footer`
with a checkbox and the "Don't show this message again," and responds
to clicks on the checkbox by setting the `this.hint.showHint` and
LocalStorage values to "false".
An example web component using it has been supplied.
* web: support dark mode for hints.
This was nifty. Still not entirely sure about the `theme="dark"`
rippling through the product, but in this case it works quite well.
All it took was defining the alternative dark mode values in a CSS
entry, `:host([theme="dark"]) { ... }` and exploiting Patternfly's
already intensely atomized CSS Custom Properties properly.
* web: revise colors to use more of the Authentik dark-mode style.
* Update web/src/components/ak-hint/ak-hint.ts
Signed-off-by: Jens L. <jens@beryju.org>
* remove any
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
---------
Signed-off-by: Jens L. <jens@beryju.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L <jens@goauthentik.io>
* web/elements: rename renderInlineForm to renderForm set submit handler to empty function
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix all kinds of forms not using the form inheritance correctly
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
---------
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* web: laying the groundwork for future expansion
This commit is a hodge-podge of updates and changes to the web. Functional changes:
- Makefile: Fixed a bug in the `help` section that prevented the WIDTH from being accurately
calculated if `help` was included rather than in-lined.
- ESLint: Modified the "unused vars" rule so that variables starting with an underline are not
considered by the rule. This allows for elided variables in event handlers. It's not a perfect
solution-- a better one would be to use Typescript's function-specialization typing, but there are
too many places where we elide or ignore some variables in a function's usage that switching over
to specialization would be a huge lift.
- locale: It turns out, lit-locale does its own context management. We don't need to have a context
at all in this space, and that's one less listener we need to attach t othe DOM.
- ModalButton: A small thing, but using `nothing` instead of "html``" allows lit better control over
rendering and reduces the number of actual renders of the page.
- FormGroup: Provided a means to modify the aria-label, rather than stick with the just the word
"Details." Specializing this field will both help users of screen readers in the future, and will
allow test suites to find specific form groups now.
- RadioButton: provide a more consistent interface to the RadioButton. First, we dispatch the
events to the outside world, and we set the value locally so that the current `Form.ts` continues
to behave as expected. We also prevent the "button lost value" event from propagating; this
presents a unified select-like interface to users of the RadioButtonGroup. The current value
semantics are preserved; other clients of the RadioButton do not see a change in behavior.
- EventEmitter: If the custom event detail is *not* an object, do not use the object-like semantics
for forwarding it; just send it as-is.
- Comments: In the course of laying the groundwork for the application wizard, I throw a LOT of
comments into the code, describing APIs, interfaces, class and function signatures, to better
document the behavior inside and as signposts for future work.
* web: permit arrays to be sent in custom events without interpolation.
* actually use assignValue or rather serializeFieldRecursive
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* web: package up horizontal elements into their own components.
This commit introduces a number of "components." Jens has this idiom:
```
<ak-form-element-horizontal label=${msg("Name")} name="name" ?required=${true}>
<input
type="text"
value="${ifDefined(this.instance?.name)}"
class="pf-c-form-control"
required
/>
</ak-form-element-horizontal>
```
It's a very web-oriented idiom in that it's built out of two building blocks, the "element-horizontal" descriptor,
and the input object itself. This idiom is repeated a lot throughout the code. As an alternative, let's wrap
everything into an inheritable interface:
```
<ak-text-input
name="name"
label=${msg("Name")}
value="${ifDefined(this.instance?.name)}
required
>
</ak-text-input>
```
This preserves all the information of the above, makes it much clearer what kind of interaction we're having
(sometimes the `type=` information in an input is lost or easily missed), and while it does require you know
that there are provided components rather than the pair of layout-behavior as in the original it also gives
the developer more precision over the look and feel of the components.
*Right now* these components are placed into the LightDOM, as they are in the existing source code, because
the Form handler has a need to be able to "peer into" the "element-horizontal" component to find the values
of the input objects. In a future revision I hope to place the burden of type/value processing onto the
input objects themselves such that the form handler will need only look for the `.value` of the associated
input control.
Other fixes:
- update the FlowSearch() such that it actually emits an input event when its value changes.
- Disable the storybook shortcuts; on Chrome, at least, they get confused with simple inputs
- Fix an issue with precommit to not scan any Python with ESLint! :-)
* web: provide storybook stories for the components
This commit provides storybook stories for the ak-horizontal-element wrappers. A few
bugs were found along the way, including one rather nasty one from Radio where we
were still getting the "set/unset" pair in the wrong order, so I had to knuckle down
and fix the event handler properly.
* web: test oauth2 provider "guinea pig" for new components
I used the Oauth2 provider page as my experiment in seeing if the
horizontal-element wrappers could be used instead of the raw wrappers
themselves, and I wanted to make sure a test existed that asserts
that filling out THAT form in the ProvidersList and ProvidersForm
didn't break anything.
This commit updates the WDIO tests to do just that; the test is
simple, but it does exercise the `name` field of the Provider,
something not needed in the Wizard because it's set automatically
based on the Application name, and it even asserts that the new
Provider exists in the list of available Providers when it's done.
* web: making sure ESlint and Prettier are happy
* "fix" lint
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
---------
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
* web: laying the groundwork for future expansion
This commit is a hodge-podge of updates and changes to the web. Functional changes:
- Makefile: Fixed a bug in the `help` section that prevented the WIDTH from being accurately
calculated if `help` was included rather than in-lined.
- ESLint: Modified the "unused vars" rule so that variables starting with an underline are not
considered by the rule. This allows for elided variables in event handlers. It's not a perfect
solution-- a better one would be to use Typescript's function-specialization typing, but there are
too many places where we elide or ignore some variables in a function's usage that switching over
to specialization would be a huge lift.
- locale: It turns out, lit-locale does its own context management. We don't need to have a context
at all in this space, and that's one less listener we need to attach t othe DOM.
- ModalButton: A small thing, but using `nothing` instead of "html``" allows lit better control over
rendering and reduces the number of actual renders of the page.
- FormGroup: Provided a means to modify the aria-label, rather than stick with the just the word
"Details." Specializing this field will both help users of screen readers in the future, and will
allow test suites to find specific form groups now.
- RadioButton: provide a more consistent interface to the RadioButton. First, we dispatch the
events to the outside world, and we set the value locally so that the current `Form.ts` continues
to behave as expected. We also prevent the "button lost value" event from propagating; this
presents a unified select-like interface to users of the RadioButtonGroup. The current value
semantics are preserved; other clients of the RadioButton do not see a change in behavior.
- EventEmitter: If the custom event detail is *not* an object, do not use the object-like semantics
for forwarding it; just send it as-is.
- Comments: In the course of laying the groundwork for the application wizard, I throw a LOT of
comments into the code, describing APIs, interfaces, class and function signatures, to better
document the behavior inside and as signposts for future work.
* web: permit arrays to be sent in custom events without interpolation.
* actually use assignValue or rather serializeFieldRecursive
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
---------
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
web/improve testability
This is a trio of small hacks that allow the E2E tests to find several components
on the page while the test is running:
- Add a `data-managed-for` field to SearchSelect's positioned elements. If a search
has a `name` field, it will be reflected here, allowing tests to find specific
instances of the dropdown elements.
- Add a forwarder to the search select wrappers we use for our SearchSelect.
- Added aria details to the UserLibrary header to make it easy to identify.
* Web: Detangling some circular dependencies in Admin and User
Admin, User, and Flow should not dependend upon each other, at least
not in a circular way. If Admin and User depend on Flow, that's
fine, but Flow should not correspondingly depend upon elements of
either; if they have something in common, let's put them in
`@goauthentik/common` or find some other smart place to store them.
This commit refactors the intentToLabel and actionToLabel functions
into `@goauthentik/common/labels` and converts them to static tables
for maintenance purposes.
* web: "Consistency is the hobgoblin of small minds" - Ralph Waldo Emerson
* web: I found these confusing to look at, so I added comments.
* web: remove admin-to-user component reference(s)
There was only one: AppIcon. This has been moved to `components`.
Touching the LibraryApplications page triggered a cyclomatic
complexity check. Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.
* web: remove admin from elements
This commit removes the two references from `elements` to `admin`: the list of UserEvents and a
reference to the FlowSearch type, used by the Forms manager to decide how to extract a value.
For FlowSearch, a different convention for detecting the type was implemented (instances of the
object have a unique fieldname for the value holder). UserEvents and ObjectChangelog have been
moved to `components` as they're clearly dependent upon the API.
This defers work on removing Admin from Components, as that is (again) references going the
wrong way, but that can happen later.
* web: remove admin-to-user component reference(s) (#6856)
There was only one: AppIcon. This has been moved to `components`.
Touching the LibraryApplications page triggered a cyclomatic
complexity check. Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.
* This was supposed to be merged.
* web: remove `./element`⇢`./user` references
The offender here is UserDevicesList, which despite being in `elements` is only
used by the admin/user/UserViewPage. The problem is that UserDevicesList,
despite being in `admin`, inherits from `user`, so moving it would have created
a new admin⇢user reference, and the whole point of this exercise is to get rid
of references that point "up" from the foundational pieces to the views, or
that refer to components in sibling applications.
After examining UserDevicesList, I realized that *every feature* of MFADevicesList
had been overridden: the rows, the columns, the toolbar, and the endpoint all had
custom overrides. Nothing was left of MFADevicesList after that. Even the
property that the web component used had been completely changed. The only thing
they had in common was that they both inherited from `Table<Device>`.
Refactoring UserDevicesList so that it inherited directly from `Table<Device>` and
then moving it into `./admin/users` was the obvious and correct step.
Both used the same label table, so that went into the `common/labels` folder.
Along the way, I cleaned up a few minor details. Just little things, like the repeated invocation
of:
```
new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorAdminMETHODDestroy({ id: device.pk });
```
This is repeated five times, once for each Method. By creating these:
```
const api = new AuthenticatorsApi(DEFAULT_CONFIG);
const id = { id: device.pk };
```
The method invocation could be just `api.authenticatorsMETHODDestroy(id)`, which is easier on the
eyes. See the MFADevicesPage for the full example.
Similarly,
```
return [
new TableColumn(msg("Name"), ""),
new TableColumn(msg("Type"), ""),
new TableColumn("")
];
```
is more straightforward as:
```
const headers = [msg("Name"), msg("Type"), ""];
return headers.map((th) => new TableColumn(th, ""));
```
We've labeled what we're working with, and web developers ought to know that `th` is the HTML code
for `table header`.
I've had to alter what files are scanned in pre-commit mode; it doesn't handle renamed files very well,
and at the moment a file that is renamed is not scanned, as its "new" name is not straightforwardly
displayed, not even by `git porcelain`.
* web: make the table of column headers look like a table
* web: detangle `common` from `elements`.
And just like that, `common` no longer has a reference to `elements`. I don't mind this little bit of
code duplication if it removes a cycle. What it does point out is that there are bits of `common` that
are predicated on the presence of the browser, and that there are bits of `elements` that, if they rely
on `common`, can't be disentangled from the application as a whole. Which seems to me that we have two
different things going on in common: things about an application, and things about elements that are
independent of the application.
I'll think about those later.
```
$ rg 'import.*@goauthentik' ./common/ | perl -ne 'm{"(@goauthentik[^"]*)"} && print "$1\n"' | sort | cut -d '/' -f1-2 | uniq | sort
@goauthentik/api
@goauthentik/common
$
```
* web: odd bug; merge-related? Gonna investigate.
* web: build failure thanks to local cache; fixed
* web: detangle `components` from `admin`.
This was the last inappropriate reference: something from `./components` referencing something in
`./admin`, in this case the `ak-event-info` component. Used by both Users and Admin, moving it
into `./components` was the obvious correct step.
`ak-event-info` is a lookup table relating specific events in the event log to rich, textual
representations; in the special case of model changes and email info, even more rich content is
available in a dl/dt format. I've tableized the model changes and email info renderer, and I've
extracted every event's textual representation into its own method, converting the `switch/case`
rendering statement into a `switch/case` dispatch switch. This has the virtue of isolating each
unique case and making the dispatch switch short and coherent.
The conversion was done mechanistically; I gave the refactorer (Tide, in this case) instructions to
duplicate the switch block and then convert every case into a method with a name patterned on the
`case`. Going back to the original switch block, it was easy to duplicate the pattern matching and
convert it into a dispatch switch.
And with this, there are zero cycles in the references between the different "packageable" sections
of the UI. The only thing left to do is figure out how to redistribute `./elements` and `./components`
in a way that makes sense for each.
* Changed function name from 'emailMessageBody' to 'githubIssueMessageBody' to better reflect its usage.
* web: added comments about length and purpose of githubIssueMessageBody.
* Update web/src/common/labels.ts
Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
* Unwanted change.
* web/add tooltip buttons to user details page
This commit wraps the command buttons on the UserDetailsPage with tooltips providing greater copy
explaining what each button does. It also ensures that every button is a minimum of 11ems in width
(The longest phrase, 'Reset Password', results in a width of 10.75ems; this makes them all
consistent.)
The technique for giving the `ak-action-button` objects a mininum width uses the CSS `::part()`
syntax, which is new. CanIUse shows that it's at 95.3% of global usage; our weak points remain Opera
Mini and UC Browser for Android.
Oh, and IE. But the various Powers That Be™ agree we're no longer tracking or caring about IE.
* I added some text, so it's my responibility to add the language files.
* fix text
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* rework
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* web: enforce a max-width on the container for the buttons so that they don't look funky on ultrawide monitors.
* wbe: re-ran and confirmed prettier.
---------
Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
* Web: Detangling some circular dependencies in Admin and User
Admin, User, and Flow should not dependend upon each other, at least
not in a circular way. If Admin and User depend on Flow, that's
fine, but Flow should not correspondingly depend upon elements of
either; if they have something in common, let's put them in
`@goauthentik/common` or find some other smart place to store them.
This commit refactors the intentToLabel and actionToLabel functions
into `@goauthentik/common/labels` and converts them to static tables
for maintenance purposes.
* web: "Consistency is the hobgoblin of small minds" - Ralph Waldo Emerson
* web: I found these confusing to look at, so I added comments.
* web: remove admin-to-user component reference(s)
There was only one: AppIcon. This has been moved to `components`.
Touching the LibraryApplications page triggered a cyclomatic
complexity check. Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.
* web: remove admin from elements
This commit removes the two references from `elements` to `admin`: the list of UserEvents and a
reference to the FlowSearch type, used by the Forms manager to decide how to extract a value.
For FlowSearch, a different convention for detecting the type was implemented (instances of the
object have a unique fieldname for the value holder). UserEvents and ObjectChangelog have been
moved to `components` as they're clearly dependent upon the API.
This defers work on removing Admin from Components, as that is (again) references going the
wrong way, but that can happen later.
* web: remove admin-to-user component reference(s) (#6856)
There was only one: AppIcon. This has been moved to `components`.
Touching the LibraryApplications page triggered a cyclomatic
complexity check. Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.
* This was supposed to be merged.
* web: remove `./element`⇢`./user` references
The offender here is UserDevicesList, which despite being in `elements` is only
used by the admin/user/UserViewPage. The problem is that UserDevicesList,
despite being in `admin`, inherits from `user`, so moving it would have created
a new admin⇢user reference, and the whole point of this exercise is to get rid
of references that point "up" from the foundational pieces to the views, or
that refer to components in sibling applications.
After examining UserDevicesList, I realized that *every feature* of MFADevicesList
had been overridden: the rows, the columns, the toolbar, and the endpoint all had
custom overrides. Nothing was left of MFADevicesList after that. Even the
property that the web component used had been completely changed. The only thing
they had in common was that they both inherited from `Table<Device>`.
Refactoring UserDevicesList so that it inherited directly from `Table<Device>` and
then moving it into `./admin/users` was the obvious and correct step.
Both used the same label table, so that went into the `common/labels` folder.
Along the way, I cleaned up a few minor details. Just little things, like the repeated invocation
of:
```
new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorAdminMETHODDestroy({ id: device.pk });
```
This is repeated five times, once for each Method. By creating these:
```
const api = new AuthenticatorsApi(DEFAULT_CONFIG);
const id = { id: device.pk };
```
The method invocation could be just `api.authenticatorsMETHODDestroy(id)`, which is easier on the
eyes. See the MFADevicesPage for the full example.
Similarly,
```
return [
new TableColumn(msg("Name"), ""),
new TableColumn(msg("Type"), ""),
new TableColumn("")
];
```
is more straightforward as:
```
const headers = [msg("Name"), msg("Type"), ""];
return headers.map((th) => new TableColumn(th, ""));
```
We've labeled what we're working with, and web developers ought to know that `th` is the HTML code
for `table header`.
I've had to alter what files are scanned in pre-commit mode; it doesn't handle renamed files very well,
and at the moment a file that is renamed is not scanned, as its "new" name is not straightforwardly
displayed, not even by `git porcelain`.
* web: make the table of column headers look like a table
* web: detangle `common` from `elements`.
And just like that, `common` no longer has a reference to `elements`. I don't mind this little bit of
code duplication if it removes a cycle. What it does point out is that there are bits of `common` that
are predicated on the presence of the browser, and that there are bits of `elements` that, if they rely
on `common`, can't be disentangled from the application as a whole. Which seems to me that we have two
different things going on in common: things about an application, and things about elements that are
independent of the application.
I'll think about those later.
```
$ rg 'import.*@goauthentik' ./common/ | perl -ne 'm{"(@goauthentik[^"]*)"} && print "$1\n"' | sort | cut -d '/' -f1-2 | uniq | sort
@goauthentik/api
@goauthentik/common
$
```
* web: odd bug; merge-related? Gonna investigate.
* web: build failure thanks to local cache; fixed
* web: detangle `components` from `admin`.
This was the last inappropriate reference: something from `./components` referencing something in
`./admin`, in this case the `ak-event-info` component. Used by both Users and Admin, moving it
into `./components` was the obvious correct step.
`ak-event-info` is a lookup table relating specific events in the event log to rich, textual
representations; in the special case of model changes and email info, even more rich content is
available in a dl/dt format. I've tableized the model changes and email info renderer, and I've
extracted every event's textual representation into its own method, converting the `switch/case`
rendering statement into a `switch/case` dispatch switch. This has the virtue of isolating each
unique case and making the dispatch switch short and coherent.
The conversion was done mechanistically; I gave the refactorer (Tide, in this case) instructions to
duplicate the switch block and then convert every case into a method with a name patterned on the
`case`. Going back to the original switch block, it was easy to duplicate the pattern matching and
convert it into a dispatch switch.
And with this, there are zero cycles in the references between the different "packageable" sections
of the UI. The only thing left to do is figure out how to redistribute `./elements` and `./components`
in a way that makes sense for each.
* Changed function name from 'emailMessageBody' to 'githubIssueMessageBody' to better reflect its usage.
* web: added comments about length and purpose of githubIssueMessageBody.
* Update web/src/common/labels.ts
Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
* Unwanted change.
---------
Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
Co-authored-by: Jens L. <jens@goauthentik.io>
* Web: Detangling some circular dependencies in Admin and User
Admin, User, and Flow should not dependend upon each other, at least
not in a circular way. If Admin and User depend on Flow, that's
fine, but Flow should not correspondingly depend upon elements of
either; if they have something in common, let's put them in
`@goauthentik/common` or find some other smart place to store them.
This commit refactors the intentToLabel and actionToLabel functions
into `@goauthentik/common/labels` and converts them to static tables
for maintenance purposes.
* web: "Consistency is the hobgoblin of small minds" - Ralph Waldo Emerson
* web: I found these confusing to look at, so I added comments.
* web: remove admin-to-user component reference(s)
There was only one: AppIcon. This has been moved to `components`.
Touching the LibraryApplications page triggered a cyclomatic
complexity check. Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.
* web: remove admin from elements
This commit removes the two references from `elements` to `admin`: the list of UserEvents and a
reference to the FlowSearch type, used by the Forms manager to decide how to extract a value.
For FlowSearch, a different convention for detecting the type was implemented (instances of the
object have a unique fieldname for the value holder). UserEvents and ObjectChangelog have been
moved to `components` as they're clearly dependent upon the API.
This defers work on removing Admin from Components, as that is (again) references going the
wrong way, but that can happen later.
* web: remove admin-to-user component reference(s) (#6856)
There was only one: AppIcon. This has been moved to `components`.
Touching the LibraryApplications page triggered a cyclomatic
complexity check. Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.
* This was supposed to be merged.
* web: remove `./element`⇢`./user` references
The offender here is UserDevicesList, which despite being in `elements` is only
used by the admin/user/UserViewPage. The problem is that UserDevicesList,
despite being in `admin`, inherits from `user`, so moving it would have created
a new admin⇢user reference, and the whole point of this exercise is to get rid
of references that point "up" from the foundational pieces to the views, or
that refer to components in sibling applications.
After examining UserDevicesList, I realized that *every feature* of MFADevicesList
had been overridden: the rows, the columns, the toolbar, and the endpoint all had
custom overrides. Nothing was left of MFADevicesList after that. Even the
property that the web component used had been completely changed. The only thing
they had in common was that they both inherited from `Table<Device>`.
Refactoring UserDevicesList so that it inherited directly from `Table<Device>` and
then moving it into `./admin/users` was the obvious and correct step.
Both used the same label table, so that went into the `common/labels` folder.
Along the way, I cleaned up a few minor details. Just little things, like the repeated invocation
of:
```
new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorAdminMETHODDestroy({ id: device.pk });
```
This is repeated five times, once for each Method. By creating these:
```
const api = new AuthenticatorsApi(DEFAULT_CONFIG);
const id = { id: device.pk };
```
The method invocation could be just `api.authenticatorsMETHODDestroy(id)`, which is easier on the
eyes. See the MFADevicesPage for the full example.
Similarly,
```
return [
new TableColumn(msg("Name"), ""),
new TableColumn(msg("Type"), ""),
new TableColumn("")
];
```
is more straightforward as:
```
const headers = [msg("Name"), msg("Type"), ""];
return headers.map((th) => new TableColumn(th, ""));
```
We've labeled what we're working with, and web developers ought to know that `th` is the HTML code
for `table header`.
I've had to alter what files are scanned in pre-commit mode; it doesn't handle renamed files very well,
and at the moment a file that is renamed is not scanned, as its "new" name is not straightforwardly
displayed, not even by `git porcelain`.
* web: make the table of column headers look like a table
* web: detangle `common` from `elements`.
And just like that, `common` no longer has a reference to `elements`. I don't mind this little bit of
code duplication if it removes a cycle. What it does point out is that there are bits of `common` that
are predicated on the presence of the browser, and that there are bits of `elements` that, if they rely
on `common`, can't be disentangled from the application as a whole. Which seems to me that we have two
different things going on in common: things about an application, and things about elements that are
independent of the application.
I'll think about those later.
```
$ rg 'import.*@goauthentik' ./common/ | perl -ne 'm{"(@goauthentik[^"]*)"} && print "$1\n"' | sort | cut -d '/' -f1-2 | uniq | sort
@goauthentik/api
@goauthentik/common
$
```
* web: odd bug; merge-related? Gonna investigate.
* web: build failure thanks to local cache; fixed
* Unwanted change.