admin@0.2.0
Minor Changes
-
29c9b8b: Peek at an organization beside the list. Every row in the admin organizations
list carries a peek control that docks a 400px panel next to the table instead
of leaving the page, so the search term, the filters and the scroll position
all survive the look-up. The panel shows account type, trial end, member count,
creation date and both ids, with a copy control beside each id that confirms
with a check. The table narrows to Name, Slug and Type while the panel is open,
and gives the operator's own column choices straight back when it closes.The control takes the mouse and the keyboard alike. It reports whether its own
panel is open, and it closes the panel it opened. Arrow Down and Arrow Up walk
the panel through the rows on screen and scroll each one into view, and Escape
closes it. Every close puts the keyboard back on the control that opened the
panel. A screen reader is told which organization the panel is showing, each
time the panel opens, moves to another row, or closes.
Patch Changes
-
e0baacf: Let a platform admin set one account type on many organizations at once. Ticking
rows in the organizations list turns the strip above the table into a bulk
control: it counts what is ticked, offers the account types the server accepts,
and reads the count and the target type back in a confirmation before anything is
written. Nothing is written until that confirmation is accepted.The ids come from the ticked rows and from nowhere else. There is no field that
takes an id, because the write matches an id case-sensitively while the list
search matches case-insensitively: an id pasted in the wrong case would come back
as missing while the row it names sat on screen.An id the server matched no organization to is named on screen after the write,
by the organization the operator ticked, rather than being counted silently as
done. The selection is dropped whenever the operator pages, sorts or filters, so
an account type cannot be set on a record that scrolled out of view.Row selection is opt-in for the shared admin table. A page that does not ask for
it renders exactly as before. The checkbox column is pinned to the left edge, the
way the actions column is pinned to the right, because the list is wider than the
window and the control that picks rows would otherwise scroll out of reach. -
e38546e: The admin organizations list now filters on sets rather than one value at a time:
account_typestakes several account types,trial_statestakes any of running, ending soon, expired, demoted, converted or none, anddisabled_statestakes active, disabled or both. An empty set means no filter, exceptdisabled_states, where an empty set still means active only. A value outside the known list matches nothing rather than failing the request, so an operator pasting a colleague's URL gets an empty table instead of an error. The total reported alongside the page counts the same filtered set, trial state included.This is an expand step. The single-valued
account_typeand theinclude_disabledboolean both still work:account_typejoinsaccount_typesas one more member of the same set, anddisabled_statesoverridesinclude_disabledwhen supplied. Keeping them lets the server ship before the dashboard changes. Retiring them is a separate contraction step. -
83ca4ea: Gather the organizations list's two row controls into one Actions column, pinned
to the right edge of the table. Peek and the row menu used to sit in two
separate columns ahead of the name, which put two controls between the operator
and the thing they came to read, and left the row's first column carrying
nothing they could name. They are now one cell at the end of the row, peek
first, sized to its contents so it does not read as an empty gutter.Pinned rather than merely last, because the list is wider than most windows. A
trailing column that scrolled with the rest would start life off the right edge:
in a 760 pixel wide window it sat 227 pixels past the edge of the viewport, so
neither control could be seen or clicked until the operator scrolled the table
sideways. Pinned, both are on screen from the start and hold their position
while the columns scroll underneath them.The pinned cell takes its colour from the row it belongs to rather than a flat
one of its own, so it stays invisible as a cell: it matches the highlight on the
row being peeked at, and it matches the hover. To give it something opaque to
inherit, rows in the organizations list now carry a background of their own, and
their hover and open-menu highlights became fully opaque. A translucent
highlight was painted twice on the pinned cell, once by the row and once by the
cell stacked above it, which made it visibly darker than the rest of its row and
let the scrolled columns show through it. Only this list changes; every other
admin table keeps the highlights it had.Alt+click a row to peek at it, which the peek panel's first release had and then
lost. Plain click still opens the organization, and the peek button in the
Actions column still peeks, so the gesture is a shortcut rather than the only
way in. It works on any part of the row including the organization's name, whose
link would otherwise treat Alt+click as "save link as" and start a download;
verified in Chromium that the download does not happen. Holding Alt together
with Ctrl, Cmd or Shift does not peek, but it still cancels that download rather
than leaving the operator with an HTML file they did not ask for.Ctrl, Cmd and Shift without Alt still belong to the link, so opening an
organization in a new tab or window works as it did. Used anywhere else in the
row they now do nothing. Until now they fell through and navigated the list away
in the current tab, which took the list out from under the tab the operator was
opening.The Actions column cannot be hidden from the Columns menu, which would otherwise
put peek and every write out of reach of the whole list. -
b5c1aa8: Let a platform admin narrow the organizations list by account type, trial state
and status, picking as many values in each as they need.The toolbar carries one control per group. Any of them opens the same sheet, on
the group that was pressed, and each group is a multi-select: Type takes any of
free, pro and enterprise, Trial takes any of the six states the rows show, and
Status takes active, disabled or both. An empty group is a default rather than
an empty result, and each control says its own: all types, all trial states,
active only.The trial options read from the same map the trial badge on the row renders, so
the filter and the rows it returns cannot say different words for one state.Nothing reaches the table until the operator applies. Picking three values
otherwise costs three requests and shows two lists nobody asked for on the way
to the one they did. Escape closes the sheet, discards the edit and puts the
keyboard back on the control it opened from. Clear all resets every filter and
leaves the search term alone: the term is not a filter this sheet holds.The chosen filters live in the URL, so an operator can paste the view they are
looking at and get the same rows back. Applying returns to the first page and
keeps the sort. Values are ordered as the pickers offer them and deduplicated,
so two operators who chose the same filter in a different order send one request
and share one cache entry. An account type from outside the offered list is kept
rather than dropped, because dropping it would widen a link's view while the
control still read "all types".The request now sends
account_types,trial_statesanddisabled_states, each
repeated once per chosen value, in place of the singleaccount_typeand the
include_disabledflag. -
4bc2406: Fix several ways the organizations list left a keyboard operator without an
answer while the peek panel was open.The panel is now a tab stop with a visible focus ring. Arrow Up and Arrow Down
on it walk the peek from record to record, and before this the only way to reach
it was the focus it took when it opened: one Tab to the close button and record
navigation was gone until the operator went back to the row's peek control.The panel's arrow keys now reach only the panel itself and the peek control of
the row it is showing. Every other control under the list keeps its own keys
back: Arrow Down on the pager, or on a button inside the panel body, scrolls the
way it does anywhere else instead of walking the panel to a record the operator
is not looking at and swallowing the scroll on the way.Escape is scoped wider, because it has no scrolling of its own to lose. It
closes the panel from anywhere inside it, the buttons in its body included, and
puts the keyboard back on the peek control of the row that was open. It still
does nothing on the pager, and a tooltip or menu opened from inside the panel
still takes the first Escape for itself.The Columns control also works again while the panel is open. The panel
overrides six columns: it hides five, and it forces Name on. A menu click on
any of those used to do nothing an operator could see, because the panel's
override outranked it. Checking one the panel hides snapped the checkbox back
with no column and nothing said, and unchecking Name was worse: that one looked
like nothing had happened, then took the Name column away later, when the panel
closed.Toggling any of the six now closes the panel and applies the change in the
same moment, and says which way it went, because toggling a column is asking
for that column. A column the panel does not override leaves the panel where it
is, and the keyboard stays in the Columns menu either way. -
657bbd6: Show a platform admin how much work the organizations list is holding, in a
strip of three figures above the table.Organizations counts every organization on the platform and says how many were
created in the last seven days. Trials ending in 7 days counts the trials in the
ending_soonstate. Disabled counts the switched-off organizations and says how
many were switched off in the last seven days.Each figure is a control. Pressing one filters the table to the rows behind it,
replacing whatever was filtered rather than adding to it, and clearing the
search term the figure never counted.The figures describe the whole platform, so they do not change when you filter
the table. Disabling an organization, re-enabling one or extending a trial moves
a figure, so each of those refreshes the strip.Applying a filter set, from a figure or from the filter sheet, now returns to
the first page even where the set applied is the one already on.The Status control now names the view it is showing, "Active and disabled",
where it used to count it as "2 selected". -
afb8e53: Let a platform admin disable an organization, re-enable it, and extend its
enterprise trial without leaving the organizations list.Every row carries a menu with those actions, and the peek panel repeats them as
buttons in its footer, so the operator acts on the record they are already
reading. The menu offers Disable or Re-enable, never both: which one it shows
follows the row, so a record disabled a moment ago offers the way back
immediately. Extend trial is offered only for a trial that is running or ending
soon, and never for a disabled organization, whose trial runs on while every
member is locked out.Disabling asks for a confirmation first, because it takes Gram away from every
member of the organization until it is re-enabled. Re-enabling does not: it
gives access back, and a second press costs a request and nothing else.Extending asks for a day count, starting at 14. The days are added to the date
the trial ends on now rather than to today, so extending it early does not
shorten it. A count outside 1 to 365 is refused in the dialog, with the reason,
before a request the server would reject leaves the browser.The list repaints from what the write answered, with no second request behind
it, so the row and the panel show the new state as soon as it lands. A read
already in flight when the operator acts is dropped rather than left to finish,
because it answers with the row as it was and would put that back over the
write.A write that failed says why. A write made from a dialog reports inside that
dialog. Re-enable has no dialog, so its failure raises a message above the list
that stays until it is dismissed or the next write succeeds. Every outcome also
goes through the polite region the list already announces the peek with, and the
keyboard returns to the control that opened the dialog rather than to the top of
the page. -
6d39432: A pasted organization id or WorkOS id in the admin organizations search now finds a disabled organization, because investigating a suspended organization is a leading reason to paste an id. Both id matches also ignore casing, so an id that arrived lowercased from a log pipeline still lands on the right organization. The search term is trimmed of surrounding whitespace, and % and _ inside it match literally instead of acting as wildcards. An id match still respects the account type, trial state and cursor filters.
-
d53ce10: The admin organizations search now matches a pasted organization id or WorkOS id, so an engineer who pastes an id lands on that organization instead of an empty list. Both id matches are exact; name and slug keep matching as a case-insensitive substring.
-
cff9f27: Scroll a long list in the admin app and the table moves, not the page. The
search box, the filters and the pagination row hold their places while the rows
travel under them, and the sidebar and the page header stay on screen
throughout.Until now a list longer than the window grew the whole page instead. Reaching
the fiftieth organization carried the search controls off the top of the screen
and left the pagination row somewhere below the fold, so an operator who wanted
the next page, or wanted to change a filter after reading the rows, had to
scroll the document back up to find the controls again.Every page in the admin app gains the behaviour. Anything taller than the window
now scrolls within the layout, and the shell around it stays put. -
af17932: The admin organizations endpoint can now sort and page. A caller names a column, a direction and a 1-based page number, and gets that page back together with the number of organizations the filters matched. Sortable columns are name, slug, account type, member count, created date, disabled date and trial end date; an unknown column or direction falls back to the default order rather than failing a shared link. Cursor paging is untouched and still runs the deployed dashboard, so both walks work until the client half lands.
-
5cb678d: Show an organization's real trial state in the admin dashboard. The
organizations list, the peek panel and the organization detail page all read
trial_stateandtrial_ends_atinstead offree_trial_ends_at, which was
defaulted for every organization and so reported a trial that never happened.The list column is now headed
Trialrather thanTrial ends, and reads as a
state: a badge carryingRunning,Ending soon,Expired,Demotedor
Converted, withendsand the date beside it only while the trial is still
live. An organization that never trialled reads as a dash.The three surfaces render one shared component, so the same organization cannot
read one way in the list and another way on its own page. The old fields stay
on the API for now; a follow-up takes them off the wire.