Skip to content

feat(ui): autosuggest for Company and Group on UserEditView#20

Merged
fdaugan merged 3 commits into
feature/vuejsfrom
norman/auto-suggest-identity
May 18, 2026
Merged

feat(ui): autosuggest for Company and Group on UserEditView#20
fdaugan merged 3 commits into
feature/vuejsfrom
norman/auto-suggest-identity

Conversation

@Terracosmos
Copy link
Copy Markdown
Collaborator

Remplace les v-text-field figés par des v-autocomplete server-backed
sur UserEditView. Deux commits, deux champs :

1. Company (ac755dd)

  • Recherche live via rest/service/id/company (300ms debounce, no-filter)
  • Disponible en mode Edit et New user
  • Fallback démo si l'endpoint répond code != 0

2. Group multi-select (5106825)

  • Recherche live via rest/service/id/group
  • Chips closables, multiple mode (~100k groupes possibles côté LDAP)
  • Normalise groups en Array<string> côté payload (gère le mix
    de raw strings et d'objets renvoyés par v-autocomplete v4)
  • Edit only (création de groupes depuis User non supportée côté API)

Pattern commun :

  • 300ms debounce, no-filter (filtrage côté API),
  • affichage name + scope en sous-ligne via slot #item,
  • Compatible Vuetify v4 (item raw direct, plus de item.raw.foo).

Détail technique gênant à mentionner : workaround thème ligojLight qui
rendait --v-theme-on-surface-variant quasi-invisible sur les v-list-item.
Règle CSS non-scoped sur .v-autocomplete__content avec !important. À
migrer vers un fix global du thème dans une PR séparée.

… user)

Replaces the plain v-text-field for form.company in UserEditView's
create and edit dialog with a Vuetify v-autocomplete that queries
rest/service/id/company as the user types (300ms debounced, no
client-side filter). Displays the company name as title and the
scope + member count chip as subtitle.

Covers 2 entries from Fabrice's todo (Identity → Edit user → Company
and Identity → New user → Company) in a single commit since the same
view handles both routes.

Includes a small temporary fallback in searchCompanies() that
surfaces a static demo list of 3 companies (Ligoj, AcmeCorp,
TechSolutions) when the API returns an empty array. This is needed
to validate the autocomplete pattern visually while the local dev
environment runs in "Demo mode" without an IAM provider configured.
The fallback only triggers when results are empty — once a real LDAP
node is attached in Admin → Nodes, the API will return real data and
the fallback becomes inert. To remove cleanly once LDAP is wired up.

Pattern Vuetify v4 compliant: the #item slot uses item.name /
item.scope / item.count directly (NOT item.raw.foo which is dead
in v4 — see SystemPluginView.vue:53 for a still-broken case).

Includes the standard non-scoped CSS workaround on
.v-autocomplete__content for the ligojLight custom theme that
defaults --v-theme-on-surface-variant to a near-white grey, making
v-list-item titles invisible inside teleported v-menu content.
…(edit only)

Replaces the read-only v-text-field that used to display groupsDisplay
on UserEditView with a Vuetify v-autocomplete (multiple + chips +
closable-chips) that queries rest/service/id/group as the user types
(300ms debounced, no client-side filter). Visible only in edit mode
(v-if="isEdit") since Fabrice's todo didn't include groups on New
User (and he said in the call: "les groupes éventuellement aussi
mais peut-être pas").

Covers chantier #3 of Fabrice's onboarding todo:
"Identity → Edit user → Groupes : Group doit être un suggest avec
liste dépliante (potentiellement des centaines de milliers d'entrées)."

Key implementation choices:
- groups is now normalised to an Array<string> of names everywhere
  (load, demo, payload). The legacy g.name || g defensive map is
  kept to absorb any object-shaped value that may slip through.
- onGroupModelUpdate resets searchQuery + results after pick/remove
  so the dropdown reverts to its no-data hint instead of keeping
  the last search "ghost" — Vuetify v4 doesn't clear the inline
  query automatically in multi-select mode.
- ensureCurrentGroupsInResults pre-seeds groupResults with stubs
  (just {name}) at mount so chips render immediately, no extra API
  roundtrip required.
- Same fallback strategy as the Company autocomplete: when the API
  returns an empty array (Demo mode without IAM provider), surface
  a small demo list (Engineering, Management, DevOps, Marketing,
  Sales) so the pattern can be validated visually. Inert once a
  real LDAP node is attached.
- Save payload adds groups: Array<string> only on edit. Not
  exercised in demo mode (save() early-returns) — to be validated
  end-to-end once LDAP is wired up.

Side improvement on the existing Company autocomplete in the same
view (single commit since same fix surface area):
- hint+persistent-hint replaced by a placeholder for visual cleanliness
- the no-data string aligned to the same formal wording as Groups

Pattern Vuetify 4 compliant: the #item slot uses item.name directly
(NOT item.raw.name which is dead in v4). The non-scoped style block
adopting .v-autocomplete__content as a workaround for the ligojLight
theme remains in place and covers both autocompletes in this view.

Known follow-ups (out of scope of this PR, tracked in memory.md):
- Move the four UI strings (placeholder, no-data, etc.) to i18n
- useFormGuard does not track groups (separate ref), so editing only
  groups without other field changes won't trigger the "unsaved
  changes" guard dialog. To unify in a follow-up.
- SystemPluginView.vue:53 (plugin-ui repo) still uses item.raw.foo
  pattern — silently broken since Vuetify v4. Separate PR.
@Terracosmos Terracosmos requested a review from fdaugan May 18, 2026 07:38
// pattern can be visually validated. The full backend integration
// will work once a real LDAP node is configured in Admin → Nodes.
if (companyResults.value.length === 0 && query) {
const DEMO = [
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The DEMO mode should not only based on the amount of items, but also based on a flag Vite variable injected only in dev mode.

Per Fabrice's review: demo data must never leak to production.
import.meta.env.DEV is true in 'vite dev', false in 'vite build', so the
fallback only kicks in during local development.
@sonarqubecloud
Copy link
Copy Markdown

@sonarqubecloud
Copy link
Copy Markdown

@fdaugan fdaugan merged commit b4c8384 into feature/vuejs May 18, 2026
4 of 5 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.

2 participants