Skip to content

feat(applications): one overview page for an application - #8

Open
rubenvdlinde wants to merge 23 commits into
refactor/upstream-shapefrom
feat/application-page
Open

rubenvdlinde wants to merge 23 commits into
refactor/upstream-shapefrom
feat/application-page

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Collaborator

Summary

The application page inside Tables redesigned after the first live test, where the same actions appeared three times and the tabs duplicated the pages list.

  • Header. Icon, name, description and the address with a copy button. One primary action, Open application. A settings menu holds edit name, icon and members, import a scheme, transfer ownership and delete.
  • No tabs. An opened page shows a breadcrumb back to the overview with the application name, and keeps Open application at hand.
  • Counts. Pages, tables and members in one line under the header.
  • Pages as a table: position in the menu, name, type, what it shows, and Design or Open. A filter appears once there are more than six pages. Add page is one menu with three kinds: a page with widgets, a table or view, a link.
  • Menu with the existing inline editor; the preview shows icon, label and target. Adding a table page or a link opens the editor with a fresh entry.
  • Tables the application works on, found through its nodes, its pages and the widgets on them: rows, views and who uses it.
  • Members: the owner and every share, with avatars.
  • Opening an application no longer closes the navigation. Pages with widgets belong to their application and are no longer listed under Views.

Stacked on #7.

Verification

  • eslint and stylelint clean; built.
  • grid-view.spec.ts adapted to the new structure (settings menu, add page menu, breadcrumb, views rule) and green: 3 passed. The spec now runs at 1600px wide because the navigation stays open and the grid needs the room for twelve columns.
  • Screenshots reviewed at desktop and phone width for the overview and an opened page; the phone header keeps clear of the navigation toggle.
  • Two console errors on the dev instance are preview and avatar storage errors of that instance, not of this page.

AI disclosure

Written with Claude Code (claude-fable-5-1), reviewed by the author.

🤖 Generated with Claude Code

remko48 and others added 22 commits October 7, 2026 17:36
Views get a type (table or grid), a grid of widgets and a slug, and a grid
view can exist without a table. Applications get a slug and an ordered menu
of items that open a view, a table or a link, an index page, and a detail
page that renders the menu and lets the owner edit it. A grid view has an
edit mode built on GridStack with header, description and table widgets,
saved as one grid on the view.

Assisted-by: ClaudeCode:claude-fable-5-1
The grid canvas now drives its column count from its own width, 12 on a
wide canvas, 6 on a medium one and 1 on a phone, and restores when the
window grows again; a reflow never writes back into the stored layout.
The context scheme export carries the slug, the menu items with their
targets as uuids, and the table-less views the menu points at; import
recreates missing grid views and remaps menu and widget targets by uuid.
The view uuid lookup no longer requires a table and returns null as it
declares. A Playwright spec walks the demo path: application, menu item,
grid view, two widgets, save, reload.

Assisted-by: ClaudeCode:claude-fable-5-1
The page headings leave room for the navigation toggle, the application
header and the applications index wrap on narrow screens, the menu editor
starts below its floating label and its rows wrap inside the dialog, and
a table widget no longer repeats the table's own title. Views without a
table, and shared views, now survive a reload of the tables list that
used to race with their own request.

Assisted-by: ClaudeCode:claude-fable-5-1
Unit tests for the View entity (grid parsing, type default, ownership
fallback, serialisation), the MenuItem entity, the ViewService create and
update validation (type, slug, grid shape and colors, table-less rules),
the ContextService menu replacement, slug handling, scheme export and
import resolution, the controller's menu item sanitiser, the permission
guards on a view without a table, and the activity manager skipping such
a view.

Assisted-by: ClaudeCode:claude-fable-5-1
Every widget type now carries the schema of its content on the server,
in GridWidgetTypes, and exposes it on /grid/widget-types. The add and
configure dialog renders one generic form from that schema, so a new
widget type needs a schema and a renderer, not a dialog. The server
validates and sanitises stored widget content against the same schema,
filling defaults and dropping unknown properties. The table widget's
target became one property; grids saved in the old shape still render
and are upgraded on their next save.

Assisted-by: ClaudeCode:claude-fable-5-1
… as navigation

Opened at /apps/tables/app/{slug} (the id still works), an application
shows its name and icon above its menu items in the left navigation and
starts on its first page; the resource overview stays reachable as the
last entry and keeps the per-user layout. The top-bar entry links to the
slug. Inside Tables the application page became a configuration page:
the address, the pages as cards with Design and Open actions, "Add page"
which creates a grid view with its menu entry, and the menu editor
inline instead of inside the application dialog. The app shell's
initial redirect now fires only from the root route, which also fixes
menu links inside a standalone application.

Assisted-by: ClaudeCode:claude-fable-5-1
… land

Stored in component data, the instance became a reactive proxy; drag
handlers armed through the proxy failed GridStack's own instance check
against the raw instance its nodes point at, so every moved widget
snapped back without an event and nothing was saved. markRaw keeps the
instance raw. The spec now drags a widget, saves, reloads and asserts
the position. The application slug lookup is scoped to the applications
the user owns or was given, since slugs are not unique across users.

Assisted-by: ClaudeCode:claude-fable-5-1
…and menu items

Views already carry a technical name; the fork added a slug next to it.
The slug is gone: applications and menu items get technical_name with the
same pattern as columns and views, a menu item derives its name from its
label unless the dialog sets one, and the standalone app shell routes on it.

Assisted-by: ClaudeCode:claude-fable-5-1
… its own schema

Title and show title move from the widget itself into its configuration,
the widget type carries a configuration schema next to the content schema,
and grids saved in the old shape read as the new one until saved again.

Assisted-by: ClaudeCode:claude-fable-5-1
Two OCS routes next to the internal ones the frontend uses: the widget
types a grid can hold, with both schemas, and the creation of a view
without a table. Documented in the OpenAPI document with their types.

Assisted-by: ClaudeCode:claude-fable-5-1
…migration dated

ContextScheme gains a factory that validates the import request at the
controller boundary, so the service takes one object instead of ten
parameters. The migration carries today's date.

Assisted-by: ClaudeCode:claude-fable-5-1
…kes no focus before the first interaction

The menu target was loaded once and kept as a snapshot, so a grid saved
through the store rendered its old widgets until a reload. The grid page
now reads the store's current copy of the view. On load the shell focused
the content area before any interaction, which drew the browser's focus
ring as a black line along the content edge; focus now moves only after
a first pointer or keyboard event.

Assisted-by: ClaudeCode:claude-fable-5-1
The overview gets one primary action, Open application, and a settings
menu for editing, importing a scheme, transferring and deleting. The
tabs are gone; an opened page shows a breadcrumb back to the overview.
Below the header: the counts, the pages as a table with a filter once
there are many, the menu with its editor, the tables the application
uses with who uses them, and the members. Opening an application keeps
the navigation open, and pages with widgets stay out of the views list.

Assisted-by: ClaudeCode:claude-fable-5-1
The frontend called internal routes for both actions; the OCS routes on api/2 already offer them.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
- Serve widget types from an injected service with labels translated on the server
- Validate a grid in one place: layout items must belong to a widget, widget ids are unique, a layout item is identified by its widget, booleans and integers are not cast
- Refuse a view without a table as the target of a data widget
- Validate the grids of imported views
- Reset the stored value for every parameter in ViewService::update, so a type sent with a sort is no longer overwritten
- Reject a malformed type or grid in ViewUpdateInput
- Remove the internal widget-types and standalone view routes

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
…and relations

Such a view lists no rows or columns, and creating rows or columns in it or importing into it is refused as not found. The views without a table of a deleted user are deleted with the user.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
- Add the views and tables a menu opens as nodes of the application, so the people it is shared with can open its pages
- Validate menu items in a MenuItemInput value object: http(s) links only, integer ids, unique technical names per menu
- Report a menu item whose target was deleted as broken
- Make technical names unique per owner and link the applications of others by id
- Load the menus of all applications in one query
- Answer invalid menu items and technical names with 400, and update an application atomically
- Export and import data widgets in their current target shape, create grid views before resolving nodes
- Declare tableId nullable and name the grid shapes in the API
- Show broken menu entries, say underscores in the technical name hints, keep pages out of the resource overview, use core heading sizes and drop the custom table styling
- Check the resources of an application in the application itself in the e2e tests

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
…ents

- Move and resize widgets from the widget menu, kept inside the grid
- Pick colors with NcColorPicker
- Show the labels the server translated instead of translating them again
- Align header text to start and end, so it follows the reading direction
- Use core heading sizes and keep the heading order

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
Table views are listed under their tables and pages are reached through their application. The first-interaction listeners are registered and removed in the App lifecycle. The grid e2e test uses a unique technical name per run and covers a move from the widget menu.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
…erview

- Count pages, tables and members in one translatable string each
- Show the members and their count to the owner only, with the owner's display name, as other people only see their own share
- Leave the nodes that a menu item opens out of the tables the application uses itself
- Give every table header a scope and the position column an accessible name
- Use the core heading size and logical padding so the layout follows the reading direction

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Remko <remko@conduction.nl>
@remko48
remko48 added this pull request to stack #12 October 12, 2026 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants