Skip to content

fix(intent): a view is an additional page, not a replacement — and a document's calendar line items render (#6547, #6482) - #6624

Merged
delchev merged 1 commit into
masterfrom
feat/intent-calendar-additional-page
Aug 9, 2026
Merged

fix(intent): a view is an additional page, not a replacement — and a document's calendar line items render (#6547, #6482)#6624
delchev merged 1 commit into
masterfrom
feat/intent-calendar-additional-page

Conversation

@delchev

@delchev delchev commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Closes #6547. Closes #6482.

Three ways a view: declaration silently cost the author something. All are the same failure class — the attribute parses, the generation is green, and the promise is simply absent from the running app.

#6547 — the calendar replaced the entity's layout

view: calendar|range was emitted as layoutType: MANAGE_CALENDAR, which overrode whatever layout the entity had resolved. For a function: Document master that meant losing the document surface entirely: line items, Print, inline process tasks (approvals fell back to the Inbox). Authors had to choose between the calendar and the document.

The calendar is now an additional page. EdmIntentGenerator emits the entity attribute calendarView and leaves layoutType at the natural MANAGE / MANAGE_MASTER / MANAGE_DOCUMENT, so every page that layout brings still exists. The landing route stays where it always was — no behaviour change for existing calendar apps — and the layout's own browse page moves down one segment:

Route Page
/<Entity> the calendar
/<Entity>/list the layout's list / master / document list
/<Entity>/create, /<Entity>/:id/edit, /:id/preview the layout's own editor — the document page for a document master

Both browse pages carry a switch to the other (goCalendar() / goList()); the personal surface mirrors it (/my/<Entity> calendar, /my/<Entity>/list list, so personalListModels no longer excludes calendar roots). calendar.js no longer emits the shared manage form — the layout owns it.

view: slots gets the same treatment (second commit). It was initially left replacing the layout on the argument that a picker is an authoring surface rather than a second way to browse — which does not survive contact with a booking document: the picker is how a booking is created, the document page is how it is worked with afterwards, and an author needs both. So slotsView replaces layoutType: MANAGE_SLOTS, slots.js stops emitting the shared form, and slot-click on a document master now lands on the document create page prefilled with the chosen datetime. /<Entity> is the picker, /<Entity>/list the layout's browse page, with a Slots⇄List toggle pair. Nothing about a view replaces a layout any more.

MANAGE_CALENDAR (and MANAGE_SLOTS) is gone from every consumer (uiCalendarModels / personalCalendarModels now key on calendarView; the shell template, calendar.js and the AppTest manifest's layout token follow). It was intent-only and never offered by the entity editor, so no hand-authored .edm can still carry it.

#6482 — a document's calendar line items rendered nothing

The line-items child declaring view: calendar emitted its panel markup and was then filtered out of secondaryDetails by name — correct on its own terms (the items have their own section), but it meant the calendar could only ever apply to a secondary child, and the declaration produced nothing at all.

The issue's option 1: the items pane honours it. The master now carries documentItemsLayout: calendar, derived from the child (the calendar: config belongs to the child, so it is never authored on the master), and the pane renders as an x-h-calendar on all three document surfaces (power, personal, partner): the same rows and the same line dialog — event-click edits that line, empty-day click adds one with the date preset, and Delete moves into the dialog since a calendar has no per-row menu. Mutually exclusive with documentItemsLayout: chat (both claim that pane), parser-rejected with a message naming both.

The row→event mapping is now one shared implementation — application-core/shell/js/services/calendarEvents.js (window.HarmoniaCalendar) — rather than a third, fourth and fifth copy of the Jackson java.time normalization. The calendar's own configuration is read at runtime from the child's detail registration, so the document page still never enumerates the child at generation time.

Verification

  • Unit: 343 green in engine-intent, including the rewritten documentMasterWithACalendarViewKeepsTheDocumentLayoutAndAddsTheCalendar, a new documentWhoseItemsChildIsACalendarRendersTheItemsPaneAsACalendar, and chatDocumentRejectsAnItemsChildThatIsAlsoACalendar.
  • IntentEngineIT: 39 green.
  • IntentEmissionCoverageIT (the emission oracle) green with new assertions: the /Leave calendar landing route, /Leave/list, /Leave/create, /my/Leave/list, both toggles, and the items calendar on the power and personal document surfaces (x-h-calendar="itemsCalCfg", HarmoniaCalendar.events, itemsDef.calendar, the dialog Delete, and the items table gone). The fixture gained a Roster / RosterItem document pair for it.
  • Live instance: generated + published a fixture app (a Document + view: calendar, and a document whose items child is a calendar) — 0 problems for the project, health Ready, and the generated shell carries /Booking → calendar, /Booking/list → the document list, /Booking/:id/editthe document editor, plus Roster-document.html with the items calendar.
  • mvn formatter:validate clean; the release-profile javadoc build clean.

A defect this surfaced, worth calling out

Extending the rule to slots made a mistake in the first commit obvious: navigation.js's PERSPECTIVE_COLLECTIONS listed uiCalendarModels / uiSlotsModels alongside the layout collections. Once a view entity keeps its layout it is a member of both, so its perspective was generated twice — the same rename path written twice and a duplicate application-perspectives contribution. Harmless on disk (identical content overwriting itself) but wrong; both view collections are dropped from that list, since a view entity is always in exactly one layout collection. Verified after the fix: exactly one perspectives/<Entity>/ per entity in the generated app.

Verified in a browser

Both views were driven end to end on a local instance against a generated + published fixture app (0 problems, health Ready), with no console errors:

  • items calendar — the pane paints the day-events with FK-resolved titles; event-click opens Edit line on that row with the dialog Delete, empty-day click opens Add line with the date preset. Header, totals and Print untouched.
  • calendar/<Entity> lands on the calendar with a List toggle, /<Entity>/list shows the document list with a Calendar toggle, and the event-click opens the document editor.
  • slots/<Entity> lands on the 3-day picker (honouring open/close/step) with a List toggle; a free slot opens /<Entity>/create?<Start>=… on the document editor, and the saved record's edit page carries Visit Items + Add, the totals footer and Print — exactly what a slots entity used to lose.

The partner document surface is a mechanical mirror of the personal one and is not separately asserted (the fixture has no partner-owned document).

…calendar line items render (#6547, #6482)

Two ways a `view: calendar` declaration silently cost the author something.

#6547 - the calendar REPLACED the entity's layout. `view: calendar|range` was
emitted as `layoutType: MANAGE_CALENDAR`, overriding whatever layout the entity
had resolved, so a `function: Document` master browsed on a calendar lost its
whole document surface: line items, Print, inline process tasks (approvals fell
back to the Inbox). Authors had to choose between the calendar and the document.

The calendar is now an ADDITIONAL page. `EdmIntentGenerator` emits the entity
attribute `calendarView` and leaves `layoutType` at the natural MANAGE /
MANAGE_MASTER / MANAGE_DOCUMENT, so every page that layout brings still exists.
The landing route stays where it was - no behaviour change for existing calendar
apps - and the layout's own browse page moves down one segment:

  /<Entity>            the calendar
  /<Entity>/list       the layout's list / master / document list
  /<Entity>/create     the layout's own editor (the document page for a document)
  /<Entity>/:id/edit   likewise

Both browse pages carry a switch to the other; the personal surface mirrors it
(/my/<Entity> calendar, /my/<Entity>/list list). `view: slots` deliberately
still replaces the layout - a slot picker is an authoring surface, not a second
way to browse. `MANAGE_CALENDAR` is gone from every consumer; it was intent-only
and never offered by the entity editor, so nothing hand-authored carries it.

#6482 - a document's LINE-ITEMS child declaring `view: calendar` emitted the
panel markup and was then filtered out of `secondaryDetails` by name (the items
child has its own section), so the declaration produced nothing at all: parsed,
generated, every status green, feature absent. The master now carries
`documentItemsLayout: calendar` (derived from the child - the `calendar:` config
belongs to the child) and the items PANE renders as an x-h-calendar on all three
document surfaces: the same rows and the same line dialog, event-click edits,
empty-day click adds with the date preset, Delete moves into the dialog. It is
mutually exclusive with `documentItemsLayout: chat` - parser-rejected, since both
claim that pane. The row-to-event mapping is now one shared implementation
(application-core `services/calendarEvents.js`), and the calendar's own config is
read at runtime from the child's detail registration, so the document page still
never enumerates the child at generation time.

Covered by EdmIntentGeneratorTest / IntentParserTest and the emission oracle
(IntentEmissionCoverageIT): the routes, the toggles, the items calendar on the
power and personal surfaces, and the calendar root keeping its own list + form.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@delchev

delchev commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Verified in a browser as well (the layer the emission oracle cannot reach), against a local instance with a generated + published fixture app — a function: Document master declaring view: calendar, and a second document whose line-items child declares it:

#6482 — the items pane (#/Roster/1/edit): the pane renders as the calendar with the three day-events placed on their dates, each titled through the FK lookup ("Ada Lovelace", not a raw id); the document header, the totals footer (Hours 24.00) and Print are untouched. Clicking an event opens Edit line on that row (Day 12/08/2026, Hours 8, Person) with the dialog Delete; clicking an empty day opens Add line with Day preset to that day (19/08/2026) and no Delete.

#6547 — the additional page (#/Booking): the nav entry still lands on the calendar, now with a List toggle; #/Booking/list shows the document list (a page that did not exist for a calendar entity before) with a Calendar toggle back; and clicking the calendar event opens the document editor — header form, Booking Items table with Add, totals footer, Print — where it previously opened the plain manage form.

No console errors or exceptions on either page.

@delchev
delchev merged commit 0289a90 into master Aug 9, 2026
10 checks passed
@delchev
delchev deleted the feat/intent-calendar-additional-page branch August 9, 2026 08:56
@delchev delchev changed the title fix(intent): a calendar view is an additional page, and a document's calendar line items render (#6547, #6482) fix(intent): a view is an additional page, not a replacement — and a document's calendar line items render (#6547, #6482) Aug 9, 2026
delchev added a commit that referenced this pull request Aug 9, 2026
…irs the #6624 merge left behind (#6629)

* fix(intent): the slot picker is an additional page too, and a view no longer double-registers its perspective

Follow-up to the calendar change, closing the same hole for `view: slots`.

`view: slots` was left replacing the layout on the argument that a picker is an
authoring surface rather than a second way to browse. That does not survive
contact with a booking document: the picker is how a booking is CREATED, the
document page is how it is worked with afterwards, and an author needs both -
exactly the choice #6547 was about. So the picker gets the calendar's treatment:
`EdmIntentGenerator` emits `slotsView` instead of `layoutType: MANAGE_SLOTS`,
`uiSlotsModels` keys on it, `slots.js` stops emitting the shared manage form (the
layout owns the editor, so slot-click on a document master now lands on the
DOCUMENT create page prefilled with the chosen datetime), the picker takes over
/<Entity> while the layout browses at /<Entity>/list, and the two carry a
Slots/List switch. `MANAGE_SLOTS` is gone from every consumer; like
MANAGE_CALENDAR it was intent-only and never offered by the entity editor.

It also fixes a defect the calendar commit introduced, which extending the rule
to slots made obvious: `navigation.js`'s PERSPECTIVE_COLLECTIONS listed
`uiCalendarModels`/`uiSlotsModels` alongside the LAYOUT collections. Now that a
view entity keeps its layout it is a member of both, so its perspective was
generated TWICE - the same rename path written twice and a duplicate
application-perspectives contribution. The two view collections are dropped from
that list; a view entity is always in exactly one layout collection.

Covered by EdmIntentGeneratorTest (a slots document master keeps MANAGE_DOCUMENT
and carries slotsView + the slot* metadata) and the emission oracle, whose fixture
gained a slots document: the /Visit picker landing route, /Visit/list, /Visit/create
resolving to the DOCUMENT editor, and both toggles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ide-template): the Java generation port keeps parity on the additional-page views

#6628 ported generateUtils.js to Java about an hour after #6624 merged, against the
pre-#6624 logic: uiCalendarModels / uiSlotsModels / personalCalendarModels /
personalListModels still keyed on MANAGE_CALENDAR / MANAGE_SLOTS, which
EdmIntentGenerator no longer emits.

Nothing is broken today - both paths are live and no consumer is switched, so the
JS path executes - but the moment consumers flip, calendar and slots pages would
silently stop being generated: an empty partition writes no files and raises no
error. GenerationParityIT could not catch it either, because no parity fixture
declares a view, so all four partitions can disagree while every fixture matches.

Key the Java collections on calendarView / slotsView (and drop the calendar
exclusion from personalListModels, matching the JS), and add a views.model
fixture - one calendar entity, one slot-picker entity, both keeping their MANAGE
layout - as a parity case. Confirmed it is a real guard: with the ModelGenerator
change reverted the case fails naming the absent AppointmentSlotsPage.js and
Appointment-slots.html; with it, both pipelines agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
delchev added a commit that referenced this pull request Aug 9, 2026
…generation port keeps parity (#6632)

* fix(intent): the slot picker is an additional page too, and a view no longer double-registers its perspective

Follow-up to the calendar change, closing the same hole for `view: slots`.

`view: slots` was left replacing the layout on the argument that a picker is an
authoring surface rather than a second way to browse. That does not survive
contact with a booking document: the picker is how a booking is CREATED, the
document page is how it is worked with afterwards, and an author needs both -
exactly the choice #6547 was about. So the picker gets the calendar's treatment:
`EdmIntentGenerator` emits `slotsView` instead of `layoutType: MANAGE_SLOTS`,
`uiSlotsModels` keys on it, `slots.js` stops emitting the shared manage form (the
layout owns the editor, so slot-click on a document master now lands on the
DOCUMENT create page prefilled with the chosen datetime), the picker takes over
/<Entity> while the layout browses at /<Entity>/list, and the two carry a
Slots/List switch. `MANAGE_SLOTS` is gone from every consumer; like
MANAGE_CALENDAR it was intent-only and never offered by the entity editor.

It also fixes a defect the calendar commit introduced, which extending the rule
to slots made obvious: `navigation.js`'s PERSPECTIVE_COLLECTIONS listed
`uiCalendarModels`/`uiSlotsModels` alongside the LAYOUT collections. Now that a
view entity keeps its layout it is a member of both, so its perspective was
generated TWICE - the same rename path written twice and a duplicate
application-perspectives contribution. The two view collections are dropped from
that list; a view entity is always in exactly one layout collection.

Covered by EdmIntentGeneratorTest (a slots document master keeps MANAGE_DOCUMENT
and carries slotsView + the slot* metadata) and the emission oracle, whose fixture
gained a slots document: the /Visit picker landing route, /Visit/list, /Visit/create
resolving to the DOCUMENT editor, and both toggles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ide-template): the Java generation port keeps parity on the additional-page views

#6628 ported generateUtils.js to Java about an hour after #6624 merged, against the
pre-#6624 logic: uiCalendarModels / uiSlotsModels / personalCalendarModels /
personalListModels still keyed on MANAGE_CALENDAR / MANAGE_SLOTS, which
EdmIntentGenerator no longer emits.

Nothing is broken today - both paths are live and no consumer is switched, so the
JS path executes - but the moment consumers flip, calendar and slots pages would
silently stop being generated: an empty partition writes no files and raises no
error. GenerationParityIT could not catch it either, because no parity fixture
declares a view, so all four partitions can disagree while every fixture matches.

Key the Java collections on calendarView / slotsView (and drop the calendar
exclusion from personalListModels, matching the JS), and add a views.model
fixture - one calendar entity, one slot-picker entity, both keeping their MANAGE
layout - as a parity case. Confirmed it is a real guard: with the ModelGenerator
change reverted the case fails naming the absent AppointmentSlotsPage.js and
Appointment-slots.html; with it, both pipelines agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant