feat: Alpine.js + Harmonia runtime UI template (list + manage)#6078
Open
delchev wants to merge 11 commits into
Open
feat: Alpine.js + Harmonia runtime UI template (list + manage)#6078delchev wants to merge 11 commits into
delchev wants to merge 11 commits into
Conversation
Adds `template-application-ui-harmonia-java`, a parallel runtime UI stack that generates a self-contained Alpine.js + Harmonia SPA (client-routed via Pinecone, no iframes/hubs) from the same `.model` as the Angular template, reusing the Java REST/DAO backend unchanged. - New generation template registered on `platform-templates` (appears in the EDM Generate picker as "Application - UI (Harmonia) - Java"). Composes the reused `template-application-rest-java` sources with a Harmonia UI source set. - View types: read-only `list`, and `manage` (CRUD list + shared create/edit form on a `baseFormPage` with 422 ValidationError -> per-field mapping, relationship dropdowns, client validation, delete-confirm). Other view types stubbed. - Reusable shell adopted from codbex-athena-app: x-h-split layout, sidebar, breadcrumb, responsive drawer, fetch entity client, error/i18n catalog. - Phase 1 embedding: Alpine 3.15.11 + Harmonia 1.24.1 + Lucide served as webjars via application-core (harmonia.version bumped 1.4.2 -> 1.24.1); Pinecone Router vendored (no webjar) under application-core/vendor (license-excluded). The generated index.html references only local assets, no CDN. - HARMONIA_RUNTIME_PLAN.md: research + phasing. IntentSettings: comment noting the intent model recipe names the template explicitly (Angular default today). Verified end-to-end against a live app from a real model (DependsOnIT/edm.model): generation succeeds, the SPA is served at /services/web/<project>/, and a CRUD round-trip + relationship dropdowns hit the generated controllers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add `setting` view type: SETTING entities (e.g. Country/City) reuse the manage CRUD templates against the uiSettingModels collection, grouped under a "Settings" sidebar section with their own routes; their derived controller path (<restBase>/settings/<Entity>Controller) matches the generated backend. - Fix: the shell index.html header comment contained a bare `$models` Velocity reference, which rendered the entire entity object graph into a comment in every generated app. Reworded to prose. Verified live: generating DependsOnIT/edm.model now produces Country/City CRUD UI (list + form pages + views), a Settings sidebar group, and /Country|/City routes; the header no longer dumps the model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… paths
Master-detail (MANAGE_MASTER/LIST_MASTER + MANAGE_DETAILS/LIST_DETAILS):
- Master entities get a master page at /<Master> — an x-h-split with the master
record list (left) and, for the selected record, its detail panels (right).
Masters reuse the manage form for their own routed create/edit.
- Details are decoupled via a runtime registry: each detail emits a registration
(App.registerDetail(<master>, {...})) so a master renders one generic detailPanel
per detail without enumerating them at generation time. The panel lists rows
filtered to the master via the controller's ?<masterEntityId>=<id> query (built
into the reused rest-java controller), with delete + routed create/edit (FK + a
returnTo preset; baseFormPage now prefills any form field from a matching query
param).
- New: detailPanel.js (shell), masterDetail.js collector, master-page/-view +
detail-register templates; App.details registry; index.html wires master nav,
routes and detail registration/form scripts.
Fix (surfaced by the hyphenated `sales-order` model): the generated SPA built
its REST base from the raw genFolderName/lowercased perspective, but the Java
backend lives under the *sanitised* package (sales-order -> sales_order). config.js
restBase now uses ${javaGenFolderName} and every page's apiPath uses
${javaPerspectiveName} — matching the DAO/REST templates. Phase 0/1 worked only
because `edm` needed no sanitising.
Verified live against sales-order.model: master Customer + two CustomerPayment
details created through the controllers, and the detail panel's filtered fetch
(?Customer=1) returns exactly that master's payments.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… equivalent)
Surfaces a process-aware record's BPM user tasks inline, the Alpine/Harmonia
counterpart of the dashboard ProcessTasks module:
- New `processTasks` Alpine store: fetches the current user's inbox tasks
(/services/inbox/tasks ?type=assignee + ?type=groups), buckets by
processInstanceId, claims candidate tasks (POST /services/inbox/tasks/{id}
{action:CLAIM}), and opens the task's formKey in an app-wide dialog (iframe),
re-fetching on close so completed tasks drop off.
- Generated list / manage / master rows render an inline inbox popover (task
count + menu) gated on $hasProcess, matching the record via
entity.ProcessId === task.processInstanceId.
- Shell wires the store script + the task-form dialog into index.html.
Verified: the store + dialog are emitted and wired; gating is correct (a
non-process model emits zero task markup); the inbox endpoints the store calls
respond 200. Full surfacing of a live task needs a process-enabled model
(deferred to combined testing).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New module — the Alpine.js + Harmonia counterpart of template-form-builder-angularjs.
Given a .form artifact it generates a standalone Harmonia form page
(gen/<genFolder>/forms/<form>/index.html + form.js), for app forms and BPM task
forms (opened by the processTasks store). Registered on platform-templates
(extension "form") so it shows in the Generate picker and can be the intent
recipe's `form` template.
Neutral form-controller contract (the decided replacement for AngularJS $scope/$http):
the .form `code` is the body of formController(ctx), with ctx.{model, params, http,
task:{id,processInstanceId,complete()}, notify, close}. Button `callback`s name
handlers attached to ctx, invoked via the page's run(). BPM task forms complete via
ctx.task.complete() -> POST /services/inbox/tasks/{id}.
v1 renders header/paragraph/line/spacer/link/textfield/textarea/number/checkbox/
date/datetime/time/color/button + one level of container-hbox/vbox; other widgets
fall back to a text input. Reuses the co-generated SPA shell's app/config/api/apiError
(../../js) + the Harmonia/Alpine/Lucide webjars.
Verified live: registers as "Harmonia Generator from Form Model"; generating the
BPMLeaveRequestIT .form produces a Harmonia page (header + textfield + two date
inputs bound to model.*, Approve/Decline buttons with data-variant + run()) and the
formController(ctx) wrapper. Documented follow-ups: migrate existing AngularJS .form
code to the ctx contract and have FormIntentGenerator emit it; feed-driven/complex
widgets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Report entities (type REPORT) run server-side via the generated Java report controller (reportFileEntity: GET / rows, POST /search, POST /export). New report.js collector emits, per report: - REPORT_TABLE (uiReportTableModels): a <Name>ReportPage + table view — data table over the report rows with a Refresh and a client-side CSV Export. - REPORT_BAR/LINE/PIE/DOUGHNUT/POLARAREA/RADAR (uiReportChartModels): a chart page that renders the rows with chart.js (already a bundled webjar, /webjars/chart.js/dist/ chart.umd.js), labelled by the report's primary-key column with one dataset per other column — mirroring the Angular report-chart mapping. Both resolve <restBase>/reports/<Name>Controller (javaPerspectiveName). index.html adds report routes, a Reports sidebar group, the page scripts and the chart.js tag. Verified non-breaking: a non-report model generates cleanly (201) with the chart.js tag present and no Reports group emitted. Full table/chart runtime needs a report-bearing model (intent-produced) — covered in combined testing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The old dashboard shell's built-in Process Inbox and Documents perspectives, ported as always-present sections of every generated Harmonia app (their backends — the platform inbox + CMS APIs — are reused unchanged): - Process Inbox (/inbox): lists the user's BPM tasks (assignee + candidate groups) via the shared processTasks store (extended with a flat `tasks` list), with search and claim-&-open into the app-wide task-form dialog. - Documents (/documents): a CMS folder browser over /services/js/documents/api — navigate folders, download files, create folder, multipart upload, delete, with a path breadcrumb. Folders vs files via type === 'cmis:folder'. Wired as static shell assets: a built-in "Inbox"/"Documents" sidebar group, the /inbox + /documents routes, and the page scripts — emitted for every app. Verified live: both views + page components serve; index.html routes/nav/scripts present; the inbox (/services/inbox/tasks) and documents (/services/js/documents/api) endpoints respond 200. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ot class-level @Listener/@scheduled The rollup, notification, integration and schedule glue templates put @Listener / @scheduled on the class, but those annotations are @target(METHOD) only (the SDK moved to method-level + self-describing interfaces). javac rejected the generated classes with "annotation interface not applicable to this kind of declaration", so engine-java failed to compile gen/events/*.java and the app's client-Java sync broke. Align them with the working Trigger template's self-describing style: - Rollup / Notification / Integration: @component implements MessageHandler with destination() + kind() instead of class-level @Listener(name=, kind=). - Job: @component implements JobHandler with cron() instead of class-level @scheduled(expression=). (Webhook's @controller is @target(TYPE) — fine; Resolver is a JavaDelegate — fine.) Verified live against dirigiblelabs/sample-intent-model: intent generate 200, glue generate 201, BookLoanCountRollupOnDelete + all gen/events classes now compile clean (no "annotation interface not applicable"), and the handlers register without instantiation errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d temporal format
A datetime-local/date/time input value (local, no zone) was POSTed raw, so a
java.time.Instant/Timestamp field failed Jackson binding ("Cannot deserialize
java.time.Instant from String 2026-06-09T16:04") — the form-create 400 the user hit.
form-page now converts both directions (matching the AngularJS stack's new Date(value)):
- toPayload() (create/edit submit): empty date -> null; an HTML date/datetime value ->
a full ISO instant (…Z) via new Date().toISOString() so Instant/Timestamp parse; a
bare TIME ("16:04", not a valid Date) passes through so a LocalTime field still parses.
- toDateInput() (edit load): the backend's full ISO value -> the value the HTML widget
expects (DATE 0..10, DATETIME-LOCAL 0..16, TIME 0..5, MONTH 0..7) so an edited record's
dates populate the fields.
Both generate per date-type property (uses a per-field temp var to avoid clashes).
Verified at generation against dirigiblelabs/sample-intent-model (Member.JoinedAt):
MemberFormPage emits the correct toPayload + toDateInput.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The detail registration baked an ABSOLUTE apiPath (App.config.restBase + ...), but
detailPanel calls api.get(def.apiPath + q) without a baseUrl override, so the client
prepended restBase again — producing /…/api/services/java/…/api/member/LoanController
(404). Make the detail apiPath relative ('/<perspective>/<Entity>Controller') like the
entity pages, so restBase is prepended exactly once.
Also guard detailPanel.load(): skip the fetch when no master is selected (masterId ==
null) so it no longer fires a wasted ?<fk>=null request before a row is picked.
Verified at generation against sample-intent-model: Loan.detail.js now registers
apiPath '/member/LoanController' (relative) -> resolves to a single
/services/java/lib/gen/library/api/member/LoanController?Member=<id>.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…EADME refresh
Consolidates documentation now that the stack is functional (Loan create/edit,
master-detail, dropdowns, dates all working):
- HARMONIA_RUNTIME_PLAN.md: new "Implementation status (built & verified)" section —
the two modules, view types + shell sections, the embedding, the runtime contracts
(REST path sanitisation, {baseUrl:''} semantics, neutral form contract, detail
registry, processTasks store, date conversion), the fixes surfaced during live
testing, and follow-ups (next up: intent glue runtime — trigger -> process-start ->
task -> complete).
- CLAUDE.md: a "Harmonia runtime UI" module section with the gotchas future work must
keep (Java-sanitised REST paths, {baseUrl:''} = prepend-nothing, two-way date
conversion, registry-driven master-detail, neutral .form code, self-describing glue
handlers) + pointers to the module READMEs and the plan doc.
- template README: status -> functional; forms row -> done via template-form-builder-harmonia.
Co-Authored-By: Claude Opus 4.8 <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.
What
Adds
template-application-ui-harmonia-java— a parallel runtime UI stack that generates a self-contained Alpine.js + Harmonia SPA (client-routed via Pinecone Router, no iframes/postMessage hubs) from the same.modelas the existing Angular template, reusing the Java REST/DAO backend unchanged. The AngularJS + BlimpKit IDE is untouched.This is the first slice of the initiative captured in
HARMONIA_RUNTIME_PLAN.md(research + phasing). Both stacks coexist by URL; the choice of UI template is explicit (Angular remains the default everywhere).What's included
platform-templates— appears in the EDM Generate picker as "Application - UI (Harmonia) - Java". Composes the reusedtemplate-application-rest-javasources with a Harmonia UI source set.list, andmanage(CRUD list + a shared create/edit form on abaseFormPagewith 422ValidationError→ per-field mapping, relationship dropdowns, client-side validation, delete-confirm dialog). Remaining view types (master-detail, report, setting, forms/BPM task forms) are stubbed as a parity checklist.codbex-athena-app:x-h-splitlayout, sidebar, route-derived breadcrumb, responsive sidebar↔drawer, afetchentity client, and a localizable error/i18n catalog.3.15.11+ Harmonia1.24.1+ Lucide1.8.0served as webjars viaapplication-core(itsharmonia.versionbumped 1.4.2 → 1.24.1 — it bundles the webjar but has no Harmonia content); Pinecone Router (no published webjar) vendored underapplication-core/.../vendor/(license-excluded in the root pom). The generatedindex.htmlreferences only local/webjars/...and/services/web/application-core/vendor/...URLs.Verification
Verified end-to-end against a live app from a real model (
DependsOnIT/edm.model,OrdersMANAGE entity with Country/City dropdowns):service-generateendpoint succeeds; the SPA is served at/services/web/<project>/gen/<model>/index.html./services/java/<project>/gen/<model>/api/<perspective>/<Entity>Controller); a live CRUD round-trip (create/list/count) + the relationship dropdown sources return 200.x-h-split/gutterless/breakpoint listener; Lucide UMD exposescreateIcons).Notes / not in scope
harmonia-viewplatform-links category remain follow-ups (tracked in the module README + plan doc).🤖 Generated with Claude Code