Repository navigation
UI review bug fixes - #388
Merged
Merged
Conversation
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The host copy of appkit's login layout wrote four data-* theme attributes even when theme_attributes is empty, so a signed-out page carried data-theme="" and mvpa.css's :root:not([data-theme]) dark-mode rules stopped matching. tag.html writes only the attributes that exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The October 2026 UI review (artifact, slice 8 "clean-up") found small defects that are still present on main after #386:
/users), an administrator cannot change a user between member and administrator: ticking the role box does nothing. The markup asks for aformStimulus controller that exists neither in the app nor in appkit.<html lang>, so browsers and screen readers do not know the page language.shared/_paginationexists but only knows thepageparameter.An administrator changes a role on
/userswith one click. Your own profile edit page has no misleading back link. Form labels speak the user's language. Every page declares its language. The accounts index uses the shared pager.Implementation
Domain terms
No new domain terms;
GLOSSARY.mdstays unchanged. No word of the intent is on anAvoid:list. UI words used in this plan:/users. It carries thehiddenattribute; its<label>is what the user clicks.shared/_paginationpartial. Page parameter: the query key one pager writes (page,own_page,external_page).langattribute of<html>, equal toI18n.localewhile the page renders.Diagrams
sequenceDiagram actor Admin Admin ->> RoleBox: click label (change event) RoleBox ->> SubmitOnChangeController: submit() SubmitOnChangeController ->> UserForm: requestSubmit() UserForm ->> UsersController: PATCH /users/:id {user: {role}} UsersController -->> Admin: redirect users_url (Turbo visit, new role label)classDiagram class UserRow["users/_user"] { form data-controller=submit-on-change } class SubmitOnChangeController { +submit() } class Pagination["shared/_pagination"] { locals: page, param = :page } class AccountsIndex["accounts/index"] { 2 tables x 2 pagers } class OtherCallers { 6 templates, 12 render sites, page only } class LoginLayout["layouts/login (host override)"] { html lang } UserRow ..> SubmitOnChangeController : data-action change->submit-on-change#submit AccountsIndex --> Pagination : param own_page / external_page OtherCallers --> Pagination : default param pageBefore:
users/_usernames aformcontroller that does not exist, so the change event goes nowhere;accounts/indexwrites its ownnav.paginationfour times;layouts/application,layouts/adminand appkit'slayouts/loginhave nolang.Signatures
app/javascript/controllers/submit_on_change_controller.js(new): submits the form it is attached to. Eager-loaded assubmit-on-changeby the existingeagerLoadControllersFrom("controllers", application).submit() // (): void— callsthis.element.requestSubmit(), so Turbo submits the form with its_method=patch.app/views/users/_user.html.erb:form_with ... data: { controller: "submit-on-change" }; the role box getsdata: { action: "change->submit-on-change#submit" }.hidden,idand thedisabledrule stay exactly as they are.app/views/shared/_pagination.html.erb: renders previous/next links for one page parameter. Strict locals become<%# locals: (page:, param: :page) %>; bothmerge(page: ...)calls becomemerge(param => ...). The six other templates that render it (twelve render sites:receipts/index,products/index,admin/users/index,shared/_recent_transactions,transactions/_table,todos/index) do not change.app/views/accounts/index.html.erb: each of the two hand-written<nav class="pagination">blocks becomes<%= render "shared/pagination", page: table[:page], param: table[:page_param] %>.app/views/users/profiles/edit.html.erb: thelink_to t("common.back"), users_pathline goes; the title-bar block stays and yields nothing, soshared/_title_barrenders no<nav>.app/views/layouts/application.html.erb,app/views/layouts/admin.html.erb:<html lang="<%= I18n.locale %>" ...>.app/views/layouts/login.html.erb(new): host override of appkit'slayouts/login, copied verbatim from the locked appkit revision (bundle show appkit, revision 9b5b30d), pluslang="<%= I18n.locale %>"on<html>. Host views win the lookup over engine views.config/locales/{en,nl,it}.yml, underactiverecord.attributes:transaction:booked_at,category_id,id,creditor,debitor,interest_at,note,original_balance_after_mutation,original_note,original_tag,type.category:direction,parent_category.chattel:purchase_transaction_id.Debit,Credit,Transferresolve to thetransactionkeys through STIlookup_ancestors; thef.labelcalls stay as they are.Words:
The nl/it words for debitor, creditor, note, booked_at and interest_at are the ones
transactions.tablealready uses.Hoofdcategoriematches the existingmust_be_parenterror text. The en values equal today's humanized output, so English pages do not change.Not checked: journal_administration has no Steepfile, and this change adds no Ruby methods.
Complexity
UsersController#update(find + update), then one redirect to the index. A disabled box fires no event and sends nothing.Invariants
submit_on_change_controller.jsand its wiring inusers/_user.disabled; enforced by the existingdisabled:expression inusers/_user(andensure_can_administeronUsersController#update).request.query_parameters.merge(param => ...)inshared/_pagination.paramleft out, the pager writespage, so the twelve other render sites render the same links as today.<html lang>equalsI18n.localeon the application, admin and login layouts;ApplicationController#switch_localesets the locale (user locale, elseI18n.default_locale,en). Appkit's session, first-run and transfer controllers inherit::ApplicationController, so the login layout gets the same locale.activerecord.attributeskeys for the three forms;test/i18n_test.rb(missing keys, normalized files) enforces it.Alternatives
A. Generic
form_controller.js(smallest): add aformcontroller withsubmit(), so the existingdata-controller="form"markup starts to work with no view change. Rejected:formsays nothing about the behaviour, and the markup relies on Stimulus' defaultinputevent for a checkbox. One line of view change buys an intention-revealing name and an explicitchangeevent.B. Host
submit-on-changecontroller (chosen): as above. One repository, no gem bump, eslint-checked. It can move to appkit later without a markup change beyond the registration.C. Appkit
submit-on-changecontroller,Appkit::FormsHelper#submit_on_change_form_with, andlangin appkit's login layout: most reusable, and no layout copy in the host. Rejected for this PR: it needs a separate appkit change and abundle update appkitbefore the host commits can go green, which the intent's constraints make stop-and-ask, and it adds public appkit API that must then stay stable. Kept as the recommended follow-up for the login layout (see## Design decisions).app/javascript/controllers/submit_on_change_controller.js— new; submits its form on change.app/views/users/_user.html.erb— wire the role box tosubmit-on-change.app/views/users/profiles/edit.html.erb— drop the Terug link.config/locales/en.yml,config/locales/nl.yml,config/locales/it.yml— the 14 attribute keys above.app/views/layouts/application.html.erb,app/views/layouts/admin.html.erb—lang.app/views/layouts/login.html.erb— new host override withlang.app/views/shared/_pagination.html.erb— optionalparamlocal.app/views/accounts/index.html.erb— render the shared pager.Tests:
test/system/user_roles_test.rb(new),test/integration/users_index_test.rb,test/integration/users/profiles_test.rb,test/integration/form_labels_test.rb(new),test/integration/page_language_test.rb(new),test/integration/pagination_navigation_test.rb.Proof
/usersticks the role box of a member; that member is now an administrator, without pressing another button →test/system/user_roles_test.rbtest "an administrator makes a member an administrator by ticking the role box"/usersunticks the role box of another administrator; that user is now a member →test/system/user_roles_test.rbtest "an administrator makes another administrator a member by unticking the role box"/usersthe role box of your own row stays disabled, and a member sees every role box disabled →test/integration/users_index_test.rbtest "your own role box is disabled",test "a member sees every role box disabled"test/integration/users/profiles_test.rbtest "your own profile edit page has no back link", existingtest "the edit form ends in an action row of Annuleren and Opslaan"/users→test/integration/users/profiles_test.rbtest "another user's profile page links back to the users list"nlopening a transaction's edit page sees Dutch labels for debitor, creditor, category, note, booked at, interest at, type and the original import fields (and id) →test/integration/form_labels_test.rbtest "the transaction form labels its fields in Dutch"nlsees Dutch labels for direction and parent category on the category form, and for purchase transaction on the chattel form →test/integration/form_labels_test.rbtest "the category form labels direction and parent category in Dutch",test "the chattel form labels the purchase transaction in Dutch"itsees Italian labels for the same fields →test/integration/form_labels_test.rbtest "the transaction form labels its fields in Italian",test "the category form labels direction and parent category in Italian",test "the chattel form labels the purchase transaction in Italian"nlgets<html lang="nl">; a user with localeengets<html lang="en">; a signed-out visitor gets the default locale →test/integration/page_language_test.rbtest "a Dutch user gets lang nl",test "an English user gets lang en",test "the admin layout declares the user's language",test "a signed-out visitor gets the default locale"test/i18n_test.rbtest_no_missing_keys,test_files_are_normalized