Skip to content

feat: save or suggest per language from the in-context dialog (pitch tolgee/tolgee-platform#3780, scope B) - #3541

Merged
Anty0 merged 6 commits into
jirikuchynka/community-sdk-suggestionsfrom
jirikuchynka/community-sdk-suggestions-b
Sep 24, 2026
Merged

Anty0 merged 6 commits into
jirikuchynka/community-sdk-suggestionsfrom
jirikuchynka/community-sdk-suggestions-b

Conversation

@Anty0

@Anty0 Anty0 commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Scope B of pitch tolgee/tolgee-platform#3780 (community suggestions from the SDK). PR into the pitch's integration branch.

Problem

A member who may edit some languages and only suggest on others, or a community contributor who may only suggest, opens the in-context dialog and gets a disabled form with "permissions not sufficient". The dialog only understands edit scopes; the suggestion backend and the community permission floor exist, but nothing in the dialog uses them.

Solution

  • Every language field gets a disposition from the foundation's getDisposition: save, suggest (the field says "Your change will be sent as a suggestion", or why, for a protected reviewed translation) or read-only.
  • One button, label following the changed set: Save / Suggest / Save & suggest; key creation keeps Create. ("Update" is now "Save".)
  • One click sends the key update for the savable fields and one create-suggestion request per changed suggest field. The key update goes first and suggestions are sent only if it succeeded — a departure from the pitch's "submit all, wait for all", accepted at review: a suggestion racing an update that toggles plural could be stored with the wrong plural-ness, and a suggestion that went through while the save failed would make the retry fail with duplicate_suggestion. Suggestions among themselves go in parallel and are all awaited.
  • A failed suggestion keeps the dialog open with the error under that field (duplicate_suggestion and suggestions_disabled have copy); fields whose suggestion went through say so and are re-seeded.
  • Suggest-only dialog: no alert, fields editable, tags hidden (state / plural / char limit / upload were already permission-gated), button "Suggest" and disabled until there is something to suggest. Clearing a translation cannot be suggested; the field says so.
  • New context action reloadKeepingEdits({ except? }) for the suggestion list (scope A): reloads the key from the server while keeping what the user typed, except in the one language named, plus unsaved tags and pending screenshots; fields are locked while it runs. commitToPage writes a saved or accepted text into the page for both the form and the list.

Pure logic (planSubmit, isSuggestOnly, keepFormFields) is unit-tested in dialogContext/tools.ts. Not built: the "Suggest instead" dropdown (optional in the pitch, skipped).

The first commit is harness only (WEB_INTERNAL_URL override so two worktrees can run web-internal e2e at once, a retrying findDcyWithCustom, presets updated for the regenerated schema, specs selecting the submit button by data-cy) and can be skipped by the reviewer.

Follow-ups on the same branch

  • refactor: one per-field error state (the simplification the reviewer asked for) — a successful suggestion is re-seeded silently, a read-only rejection shows under its field.
  • feat: whose permission is missing: current-permissions now returns userScopes (feat: backfill translation-suggestions.own-access into granular permissions (pitch tolgee/tolgee-platform#3780, scope D) tolgee-platform#3937, must merge first — the regenerated schema hunk is that PR's userId + userScopes). Where the dialog says a permission is lacking it tells the user when their account could do it but the API key / plugin sign-in cannot, and how to fix that.
  • refactor: one component per file under KeyDialog/ErrorAlert/ — a pure move, no behaviour change.
  • feat: the key description under the key name, clamped to five lines with the full text on hover (optional in the pitch, built on request).
  • test: TOLGEE_IMAGE names the server image in e2e/docker-compose.yml, defaulting to tolgee/tolgee:latest as before, so a local run can point the web-internal suite at a pre-release image without editing the file. CI is unaffected.

What it looks like

Every shot is the real in-context dialog in the SDK dev app, same key, same languages, same viewport. Before is this PR's base, the pitch's integration branch. Permissions and the language list are mocked the way the e2e specs mock them, because the released server the dev app runs against has no suggestion API yet. The same shots with more context: https://claude.ai/artifact/N7BPD6TUwppBRr6vWcs4d2

The submit button says what the click will do

The dialog no longer only saves, so the button stops claiming it does. Creating a key still says Create.

Before After
Before — Update After — Save
Update Save

A contributor who may only suggest gets a form, not a wall

With translations.suggest and suggestions on, the fields open for editing, each says where the change will go, tags are hidden, and the button reads Suggest — disabled until there is something to send. On this PR's base the same user gets read-only fields and an Update button with nothing to submit; on main, before the foundation landed, they got the "permissions not sufficient" alert.

Before After
Before — read-only fields, a button with nothing to do After — editable, every field noted as a suggestion
read-only fields, a button with nothing to do editable, every field noted as a suggestion

Saying whose permission is missing

When the account could do the thing but the credential the page uses cannot, the dialog says so and names the fix, instead of sending everyone to an admin.

Before After
Before — one wording for every cause After — the API key is the limit
one wording for every cause the API key is the limit

The key description, where the translator is looking

The description that lives on the platform now sits under the key name, clamped to five lines with the full text on hover.

Before After
Before — nothing under the key name After — the description in place
nothing under the key name the description in place

States that did not exist before

No before shot for these: on the base branch the user either could not open a working form at all, or the state had no way to arise.

One click saves what it may and suggests the rest — May translate English, may only suggest German. Changing both makes the button read Save & suggest; the key update goes first, the suggestions follow only if it succeeded.

Save & suggest, with the German field noted as a suggestion

A rejected suggestion keeps the dialog open — German was suggested twice, so the server rejects it. The error sits under that field with the text still in it, while the other fields went through and were re-seeded.

The duplicate-suggestion error under the German field

A reviewed translation on a protected project — The user may edit this language, but the translation is reviewed and the project protects reviewed strings — so the change becomes a suggestion, and the field says why.

Reviewed translations are protected, so the change is suggested

An emptied field cannot be suggested — There is no "suggest removing this translation" on the server, so a field cleared to nothing is left alone and says so.

An empty suggestion can't be sent, so the field is left unchanged

Why this one field is read-only — The key may only suggest German, while the account could translate English. The note sits on the English field, not on the whole form.

The per-field credential note on the English field

The same hint when the page is signed in through the plugin — Same rule, different fix: the copy points at the browser plugin's sign-in instead of at the API key.

The credential hint worded for the Tolgee plugin

Tests

  • jest packages/web: 32 suites, 300 tests.
  • Cypress web-internal, full suite against a local server: 7 specs, 35/35, including the new suggestForm.cy.ts (suggest-only submit, mixed save + suggest, partial failure, the credential notes, the key description).
  • The web-internal CI job pulls tolgee/tolgee:latest; the new spec mocks current-permissions and the suggestion endpoint, so it does not depend on the foundation server changes.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Essentials

Run ID: 733ddc34-8aed-4701-b03f-b76990cf4c56

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

…lation

- WEB_INTERNAL_URL (CYPRESS_WEB_INTERNAL_URL) overrides the hardcoded
  http://localhost:8114 app URL, like API_URL already did for the server, so two
  worktrees can run the suite at once
- findDcyWithCustom retries like findDcy instead of a one-shot jQuery find,
  which silently yielded an empty set when the element rendered a moment later
- mockPermissions() extracted from simulateReqAndResponse for reuse
- presets carry suggestionsMode / translationProtection, required since the
  schema regeneration
- specs locate the submit button by data-cy instead of its label
The web-internal suite could only ever run against tolgee/tolgee:latest,
so anyone needing a pre-release server had to edit the compose file.
TOLGEE_IMAGE now names the image; the default is unchanged.
Pure move ahead of the work that grows this file: ErrorAlert, severityFor,
getErrorContent, OpenExtension, DocsAPIKeys and DocsInContext each get their own
file; the index keeps every existing import path working.
A member who may only suggest on some languages, or a community
contributor who may only suggest, used to get a disabled form. Each
language field now has a disposition: editable fields are saved, fields
the user may only suggest on are sent as suggestions, the rest are
read-only. One button does both; its label follows the changed set
(Save / Suggest / Save & suggest).

The key update goes first and suggestions are sent only if it succeeded
(a suggestion racing a plural toggle could be stored with the wrong
plural-ness, and a suggestion that went through while the save failed
would make the retry fail with duplicate_suggestion). A failed suggestion
keeps the dialog open with the error under its field, and a read-only
rejection of a suggestion shows under its field instead of flipping the
whole dialog to read-only; a field whose suggestion went through is
re-seeded silently.

A suggest-only user gets a working form: no permissions alert, tags
hidden, the button disabled until there is something to suggest.

The dialog context also exposes refreshTranslation(reseedLanguage?) for
the suggestion list: it refreshes server data while keeping the other
dirty fields, unsaved tags and pending screenshots.
current-permissions now reports the user's own project scopes
(userScopes) next to the credential's. Where the dialog says a
permission is lacking — the disabled form, a read-only language field,
an operation_not_permitted error — it tells the user when their account
could do it but the API key or plugin sign-in cannot, and how to fix
that; when the account lacks it too, it says nothing about signing in.
Servers that do not send userScopes keep today's wording.
Translators who open a string in the page often have no idea what the
string is for; the description is where that context lives on the
platform, so show it right under the key name. Long descriptions are
clamped to five lines with the full text as a hover title, and the hint
next to the key name moves to the same theme colour as the description.
@Anty0
Anty0 force-pushed the jirikuchynka/community-sdk-suggestions-b branch from a7530df to 46fe2a2 Compare September 24, 2026 09:44
@Anty0
Anty0 merged commit 46fe2a2 into jirikuchynka/community-sdk-suggestions Sep 24, 2026
2 of 3 checks passed
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.

1 participant