Skip to content

[feature]: Inline constant creation in configurator#179

Open
deeonwuli wants to merge 24 commits intodevelopmentfrom
feat/inline-constant-creation-in-configurator
Open

[feature]: Inline constant creation in configurator#179
deeonwuli wants to merge 24 commits intodevelopmentfrom
feat/inline-constant-creation-in-configurator

Conversation

@deeonwuli
Copy link
Copy Markdown
Contributor

@deeonwuli deeonwuli commented Apr 20, 2026

📌 References

📝 Implementation

  • Adds an "inline constant creation" workflow to the Autogen Configurator. While editing the dataStore JSON in the Monaco editor, when the cursor lands inside a "code": "..." value (any texts.*.code reference), the suggestion widget shows a Create new constant entry. Selecting it opens a dialog to define the constant (name, short name, code, description) and saves it to DHIS2.
  • The change is also captured as a spec-driven proposal under openspec/changes/inline-constant-creation/ (proposal.md, design.md, tasks.md + delta spec) and lives long-term as openspec/specs/inline-constant-creation/spec.md.
  • Add a translations field to CreateConstantDialog so users can upload different translations.

🎨 Screenshots

Screen.Recording.2026-04-29.at.14.55.41.mov

🔥 Notes to the tester

Test against https://dev.eyeseetea.com/who-dev-41/ with a user that has F_CONSTANT_ADD (or ALL).

  1. Small config (e.g. MAL - Malaria Free): cursor inside any texts.*.code value → widget shows a single Create new constant entry. Selecting it opens the dialog. Save creates the constant in DHIS2 and inserts its code into the JSON at the cursor.
  2. Large config (e.g. TUB - Annual data, ~300KB): same as above. The "Large file detected" banner is expected, schema validation and the JSON-worker's string completions are intentionally disabled for performance.
  3. Permissions: log in as a user without F_CONSTANT_ADD. The create entry should appear marked deprecated (struck through) with detail Requires F_CONSTANT_ADD authority; selecting it should be a no-op.
  4. Validation: leave any required field blank and click Save → the form should show per-field errors and not call the API. Submitting a shortName longer than 50 characters should also block.
  5. Duplicate code error: try saving with a code that already exists in DHIS2. The dialog should surface a per-field error on code.

#869ckzqxc

@bundlemon
Copy link
Copy Markdown

bundlemon Bot commented Apr 20, 2026

BundleMon

No change in files bundle size

Groups updated (1)
Status Path Size Limits
Build Folder
./**/*
1.53MB (+5.21KB +0.33%) +20%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@deeonwuli deeonwuli changed the base branch from master to development April 21, 2026 09:57
Copy link
Copy Markdown

@xurxodev xurxodev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @deeonwuli

Solid feature PR — the layering is right (codec → entity helpers → use case → repo → UI), the Monaco completion is encapsulated in its own module, and the design doc + tests for the JSON-path walker make the trickiest piece reviewable. Most comments are about regressions in the shared ConstantRepository.save contract, error visibility, and a few anys that should be typed.

1. Should fix

  • src/data/ConstantD2Repository.ts:15-50 — chunking regression. The previous save chunked ids in groups of 100 and POSTed each chunk independently. The new buildConstantPayload collapses everything into a single POST (existing items chunked only for the fetch step, but then concatenated and sent in one shot). For the inline-create path (one item) this is fine; for ImportConstantTranslationsUseCase and any future bulk import it's a regression. Either restore chunking on the POST, or assert that save is only used for small batches and update the docstring of the repository method.
  • src/data/ConstantD2Repository.ts — empty-array behaviour. With the rewrite, save([]) now POSTs { constants: [] } (before, the chunk loop made it a no-op). Add an early return Stats.empty() when both existingConstants and newConstants are empty.
  • src/domain/common/entities/User.ts:14 — stray comment. // check if this is it looks like a leftover TODO. Remove it (or replace with the resolved decision).
  • src/webapp/.../monaco/registerInlineConstantCompletions.ts:107 — silent fetch failure. options.fetchConstants(prefix).catch(() => [] as Constant[]) swallows API errors. The user types in texts.X.code, the API fails, and they see "no constants" with no signal. Surface the failure: at minimum console.error, ideally a non-selectable completion item with detail: i18n.t("Failed to load constants").
  • src/webapp/.../monaco/registerInlineConstantCompletions.ts:25-35 — typing. The function signature uses any for monacoEditor, the model and the position are any, and the return objects are any. Use the types exported by monaco-editor (editor.IStandaloneCodeEditor, languages.CompletionItem, etc.). The dead-code fallback commandId: registeredCommandId ?? commandId (the locally-generated commandId is never registered with addCommand, so the fallback would never fire a real command) should also be removed once typing forces addCommand to return a string.
  • src/webapp/.../Editor.tsx:13,21useState<any>(). setEditorInstance and the range parameter on EditorApi.insertAtRange use any. Reuse editor.IStandaloneCodeEditor and editor.IRange instead of inventing a parallel MonacoRange shape (also duplicated in Configurator.tsx as PendingRange). Extract a small monaco/types.ts if you want to keep the import surface narrow.
  • src/webapp/.../CreateConstantDialog.tsx:189 — duplicate-code detection by regex on the error message. /code/i.test(message) && /exist|duplicate|unique/i.test(message) will silently misclassify on locale changes or DHIS2 message tweaks. The right place is the errorReports[].errorCode returned in typeReports, which Stats.errorMessage flattens away. Two options: (a) enrich Stats with structured field/error data, or (b) have CreateConstantUseCase throw a typed error that includes the field. Acceptable for v1, but mark as a known fragility (TODO + follow-up issue).
  • src/data/common/Dhis2DataStoreDataForm.ts:626 — default value vs codec contract. defaultDataStoreConfig.prefix = "" while the codec types it optional(string). Either align the default to undefined (and let consumers ?? ""), or change the codec to a non-optional string with a default. Right now the docs say "absent ⇒ no filter" but defaultDataStoreConfig materialises an empty string.

2. Recommendations non blocking

  • extractRootPrefix.ts has no unit tests. Add cases for: prefix absent, prefix as a non-string, escaped characters in the value, prefix in the middle of mid-typed (invalid) JSON. The regex fallback's .replace(/\\(.)/g, "$1") un-escapes too aggressively (\nn); fine for ASCII prefixes but worth a comment narrowing the supported character set.
  • jsonPathAtCursor.ts — extra walker tests. The 7 existing cases cover the happy path. Hand-rolled JSON traversal benefits from tests for: cursor inside a string with escaped quotes ("a\"b"), code inside an array element under texts, very deeply nested objects, and trailing comma / mid-typed malformed JSON.

The previous refactor collapsed all constants into a single POST,
regressing ImportConstantTranslationsUseCase. Re-chunk in groups of 100
on the POST step, return Stats.empty() up front when there is nothing
to save, and surface structured errorReports through Stats so callers
can react to specific field-level errors instead of regex-matching
the flattened message.
…Case

Replace the regex-on-error-message duplicate-code detection in the
configurator dialog with structured errorReports. The use case now
throws ConstantSaveError carrying the DHIS2 errorReports, and the
dialog maps each report to its corresponding form field via
errorProperty rather than parsing locale-sensitive message strings.
The codec defines `prefix` as `optional(string)`, but the in-memory
default config materialised it as the empty string and the Right
branch substituted "" when the field was absent. Keep the field
optional in the in-memory shape and pass the decoded value through
unchanged so "absent ⇒ no filter" holds end-to-end.
Add a small monaco/types.ts with structural interfaces matching the
subset of monaco-editor we use (CodeEditor, TextModel, Position,
IRange, CompletionItem). Replace the `any` types and the duplicated
range shape in Editor.tsx, Configurator.tsx, and the completion
provider with these. Drop the dead-code commandId fallback (the
local id was never registered with addCommand). When the constants
fetch fails, log the error and emit a non-selectable completion item
labelled "Failed to load constants" instead of silently showing an
empty list.
…lker

Add a spec for extractRootPrefix covering: prefix absent or empty,
non-string prefix values, escaped quotes in the value, and the
regex fallback used while the surrounding JSON is mid-typed. Extend
the jsonPathAtCursor spec with escaped quotes inside string values,
deeply nested objects, and trailing-comma / unbalanced-brace paths
the user produces while editing. Document the walker's current
behaviour around array ancestors so a future change can revisit it
intentionally.
@deeonwuli deeonwuli changed the title Feat/inline constant creation in configurator [feature]: Inline constant creation in configurator Apr 29, 2026
@deeonwuli deeonwuli marked this pull request as ready for review April 29, 2026 14:15
@deeonwuli deeonwuli requested a review from xurxodev April 29, 2026 14:16
- Add Monaco autocomplete for `texts.*.code` fields with a "Create new constant" entry.
- Introduce `CreateConstantDialog` for creating new constants inline.
- Implement auto-derivation for `shortName` and `code` based on user input.
- Ensure constant persistence via DHIS2 API with error handling for duplicates and validation.
- Gate the creation feature based on user permissions (`F_CONSTANT_ADD`).
- Maintain functionality in large-file mode by disabling unnecessary suggestions.
- Update specifications and tasks to reflect new features and requirements.
Copy link
Copy Markdown

@xurxodev xurxodev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @deeonwuli

  1. Recommended non-blocking

The IA agents configuration is duplicated, but I think this is a topic to treat in the coding dojo meeting

@deeonwuli deeonwuli requested a review from eperedo May 5, 2026 07:48
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