Skip to content

feat(contexts): save the menu of an application - #3087

Open
remko48 wants to merge 5 commits into
feat/noid/menu-modelfrom
feat/noid/menu-saving
Open

remko48 wants to merge 5 commits into
feat/noid/menu-modelfrom
feat/noid/menu-saving

Conversation

@remko48

@remko48 remko48 commented Oct 13, 2026

Copy link
Copy Markdown

Applications (contexts) can now save their menu: create and update take a menuItems list that replaces the whole menu.

What changes

  • Input (MenuItemInput): every item is validated before the service is called; an invalid item returns a 400.
    • label: required, at most 200 characters
    • icon: optional, at most 64 characters
    • target type: view, table or url (url when left out)
    • a view or table needs its positive id; a link must be an http or https address with a host, at most 2000 characters
    • technical name: optional, lowercase letters, numbers and underscores, starting with a letter
  • Saving: sending menuItems replaces the whole menu; leaving it out keeps the stored menu. The order of the list is the order of the menu.
  • Technical names: an item without one keeps the name stored for the same target, so a rename or reorder doesn't change it; otherwise one is derived from the label, with a number added when the menu already uses it. Two items sending the same name is a 400.
  • Targets become resources: the views and tables in the menu are added to the application as resources with read permission, and to its start page, so everyone the application is shared with can open them. A new target must exist and be manageable by the user; every unusable target gets the same 403, so a save reveals nothing about other people's views and tables. A stored target that no longer exists is kept and comes back broken. Removing a menu item leaves its view or table in the application as a resource.
  • Concurrent saves: an application update that loses its write lock to another request (SQLite reports "database is locked" at once) is retried.
  • Errors: invalid input on creating an application now returns a 400 instead of a 500.
  • API: menuItems is added to the create and update endpoints (OpenAPI and TS types regenerated).

🖼️ Screenshots

Backend only, no UI changes.

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔙 Backport requests are created or not needed: /backport to stableX.X
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

remko48 and others added 4 commits October 12, 2026 13:27
Creating or updating an application (context) can now set its menu. The
menu items are validated at the controller boundary, and invalid input is
answered with a 400 instead of a 500.

- every save replaces the whole menu inside the same transaction as the
  rest of the update
- the views and tables the menu opens become nodes of the application,
  with read permission, so everyone it is shared with can open them; the
  owner must be able to manage them
- a menu item without a technical name gets one derived from its label,
  with a numeric suffix when the menu already uses it
- only http and https links are accepted

Co-authored-by: Ruben van der Linde <ruben@conduction.nl>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
…hnical names

- a new menu target that does not exist gets the same refusal as one the
  owner cannot manage, so a save reveals nothing about others' ids; a target
  that was already in the menu is kept and marked broken
- an item sent without a technical name keeps the name stored for the same
  target, so renaming or reordering no longer changes it
- removing a menu item keeps its view or table as a resource, as documented
- the start page mock gets an id, which PHP 8.5 needs for the array key
- a javascript link with a host is tested, which only the scheme check stops
- the menu list is mapped with a first-class callable

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
The update runs in one transaction since the menu is saved with it. On SQLite a
concurrent request can take the write lock between its read and its write, and the
update then failed at once with a 500. It now runs again on a retryable database
error, as atomicRetry() does in core.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
@remko48
remko48 requested review from blizzz and enjeck as code owners October 13, 2026 14:38
@remko48
remko48 added this pull request to stack #3090 October 13, 2026 14:39
@remko48
remko48 force-pushed the feat/noid/menu-saving branch from 0299c48 to 80b1262 Compare October 14, 2026 07:16
@remko48
remko48 force-pushed the feat/noid/menu-saving branch from 80b1262 to 02fa6ad Compare October 14, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants