Skip to content

Commit 4fdb3ba

Browse files
authored
Merge pull request #42 from ligoj/norman/feat-ux-affordance
feat(ui): disable browser autocomplete + tooltips on DataTable icons
2 parents 3b0d6df + 6080a78 commit 4fdb3ba

11 files changed

Lines changed: 82 additions & 21 deletions

ui/src/i18n/en.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export default {
3838
'delegate.write': 'Write',
3939
'delegate.adminHelp': 'With the administration security level on this resource, the receivers of this delegation can create other delegations to share this access with other valid receivers',
4040
'delegate.writeHelp': 'With the write security level, the receivers of this delegation can modify the members of the involved groups. Without this access, this delegation grants read-only rights',
41+
'delegate.adminGranted': 'Administration granted',
42+
'delegate.writeGranted': 'Write access granted',
4143
// Fragments wrapping the receiver name in bold red on the delete
4244
// confirmation (issue #37). The host keeps the monolithic
4345
// `delegate.deleteConfirm` key intact.
@@ -51,6 +53,7 @@ export default {
5153
// in bold red.
5254
'common.bulkDeleteConfirmBefore': 'Are you sure you want to delete ',
5355
'common.bulkDeleteConfirmAfter': ' items? This cannot be undone.',
56+
'common.edit': 'Edit',
5457
// Chantier D2 — sensitive confirmations split in two fragments so the
5558
// login can be wrapped in bold red between them. The monolithic
5659
// `user.<action>Confirm` keys stay on the host side for any other
@@ -65,6 +68,8 @@ export default {
6568
'user.restoreConfirmAfter': '?',
6669
'user.resetPasswordConfirmBefore': 'Reset password for user ',
6770
'user.resetPasswordConfirmAfter': '? A new password will be sent.',
71+
'user.statusLocked': 'Locked',
72+
'user.statusActive': 'Active',
6873
'group.deleteConfirmBefore': 'Are you sure you want to delete ',
6974
'group.deleteConfirmAfter': '?',
7075
'company.deleteConfirmBefore': 'Are you sure you want to delete ',

ui/src/i18n/fr.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export default {
3131
'delegate.write': 'Écriture',
3232
'delegate.adminHelp': 'Avec le niveau de sécurité d\'administration sur cette ressource, les receveurs de cette délégation peuvent créer d\'autres délégations pour partager cet accès avec d\'autres receveurs valides',
3333
'delegate.writeHelp': 'Avec le niveau de sécurité d\'écriture, les receveurs de cette délégation peuvent modifier les membres des groupes impliqués. Sans cet accès cette délégation ne donne qu\'un droit de lecture',
34+
'delegate.adminGranted': 'Administration accordée',
35+
'delegate.writeGranted': 'Écriture accordée',
3436
// Fragments encadrant le nom du destinataire en gras-rouge dans la
3537
// confirmation de suppression (issue #37). Le host garde la clé
3638
// monolithique `delegate.deleteConfirm` intacte.
@@ -44,6 +46,7 @@ export default {
4446
// en gras-rouge pour la suppression en masse.
4547
'common.bulkDeleteConfirmBefore': 'Supprimer ',
4648
'common.bulkDeleteConfirmAfter': ' éléments ? Cette action est irréversible.',
49+
'common.edit': 'Modifier',
4750
// Chantier D2 — confirmations sensibles découpées en deux fragments
4851
// pour insérer l'identifiant en gras-rouge entre eux. Les clés
4952
// monolithiques `user.<action>Confirm` restent côté host pour
@@ -58,6 +61,8 @@ export default {
5861
'user.restoreConfirmAfter': ' ?',
5962
'user.resetPasswordConfirmBefore': 'Réinitialiser le mot de passe de l\'utilisateur ',
6063
'user.resetPasswordConfirmAfter': ' ? Un nouveau mot de passe lui sera envoyé.',
64+
'user.statusLocked': 'Verrouillé',
65+
'user.statusActive': 'Actif',
6166
'group.deleteConfirmBefore': 'Êtes-vous certain de supprimer ',
6267
'group.deleteConfirmAfter': ' ?',
6368
'company.deleteConfirmBefore': 'Êtes-vous certain de supprimer ',

ui/src/views/CompanyEditView.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
class="mb-2"
3030
no-filter
3131
clearable
32+
autocomplete="off"
3233
@update:search="onScopeSearch"
3334
>
3435
<template #item="{ props: itemProps, item }">

ui/src/views/CompanyListView.vue

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,22 @@
3434
v-model:items-per-page="itemsPerPage" :headers="headers" :items="dt.items.value" :items-length="dt.totalItems.value" :loading="dt.loading.value" item-value="name" show-select hover
3535
@update:options="loadData" @click:row="(_, { item }) => router.push('/id/company/' + item.name)">
3636
<template #item.locked="{ item }">
37-
<v-icon v-if="item.locked" color="error" size="small">mdi-lock</v-icon>
37+
<div class="text-center">
38+
<v-tooltip v-if="item.locked" :text="t('user.statusLocked')" location="top">
39+
<template #activator="{ props: tt }">
40+
<v-icon v-bind="tt" color="error" size="small">mdi-lock</v-icon>
41+
</template>
42+
</v-tooltip>
43+
</div>
3844
</template>
3945
<template #item.actions="{ item }">
4046
<v-btn icon size="small" variant="text" @click.stop="router.push('/id/company/' + item.name)">
4147
<v-icon size="small">mdi-pencil</v-icon>
48+
<v-tooltip activator="parent" :text="t('common.edit')" location="top" />
4249
</v-btn>
4350
<v-btn icon size="small" variant="text" color="error" @click.stop="startDelete(item)">
4451
<v-icon size="small">mdi-delete</v-icon>
52+
<v-tooltip activator="parent" :text="t('common.delete')" location="top" />
4553
</v-btn>
4654
</template>
4755
</LigojDataTableServer>
@@ -105,8 +113,8 @@ let lastOptions = {}
105113
const headers = computed(() => [
106114
{ title: t('common.name'), key: 'name', sortable: true },
107115
{ title: t('group.scope'), key: 'scope', sortable: false },
108-
{ title: t('group.members'), key: 'count', sortable: false, width: '100px' },
109-
{ title: t('group.locked'), key: 'locked', sortable: false, width: '80px' },
116+
{ title: t('group.members'), key: 'count', sortable: false, width: '100px', align: 'center' },
117+
{ title: t('group.locked'), key: 'locked', sortable: false, width: '80px', align: 'center' },
110118
{ title: '', key: 'actions', sortable: false, width: '120px', align: 'center' },
111119
])
112120

ui/src/views/DelegateEditDialog.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
</v-col>
3434
<v-col cols="12" sm="7">
3535
<v-autocomplete v-model="form.receiver" v-model:search="receiverSearch" :label="t('delegate.receiver')" :items="receiverDisplayItems" item-title="label" item-value="id"
36-
:loading="receiverLoading" :rules="[rules.required]" no-filter clearable auto-select-first variant="outlined" class="mb-2" @update:search="onReceiverSearch"
36+
:loading="receiverLoading" :rules="[rules.required]" no-filter clearable auto-select-first variant="outlined" class="mb-2" autocomplete="off" @update:search="onReceiverSearch"
3737
@update:menu="onReceiverMenu" />
3838
</v-col>
3939
</v-row>
@@ -62,7 +62,7 @@
6262
<v-text-field v-if="form.type === 'TREE'" v-model="form.name" :label="t('delegate.resource')" :rules="[rules.required]" :hint="t('delegate.resourceDnHint')" persistent-hint
6363
variant="outlined" class="mb-2" />
6464
<v-autocomplete v-else v-model="form.name" v-model:search="resourceSearch" :label="t('delegate.resource')" :items="resourceDisplayItems" item-title="label" item-value="id"
65-
:loading="resourceLoading" :rules="[rules.required]" :hint="t('delegate.resourceHint')" persistent-hint no-filter clearable auto-select-first variant="outlined" class="mb-2"
65+
:loading="resourceLoading" :rules="[rules.required]" :hint="t('delegate.resourceHint')" persistent-hint no-filter clearable auto-select-first variant="outlined" class="mb-2" autocomplete="off"
6666
@update:search="onResourceSearch" @update:menu="onResourceMenu" />
6767
</v-col>
6868
</v-row>

ui/src/views/DelegateListView.vue

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,47 @@
3636
for some rows, so normalize before the TYPE_ICONS lookup —
3737
same rationale as DelegateEditDialog.loadOnOpen(). -->
3838
<template #item.receiver="{ item }">
39-
<v-icon size="small" class="me-1">{{ TYPE_ICONS[item.receiverType?.toUpperCase()] || 'mdi-account' }}</v-icon>
39+
<v-tooltip :text="t('delegate.type.' + (item.receiverType || '').toLowerCase())" location="top">
40+
<template #activator="{ props: tt }">
41+
<v-icon v-bind="tt" size="small" class="me-1">{{ TYPE_ICONS[item.receiverType?.toUpperCase()] || 'mdi-account' }}</v-icon>
42+
</template>
43+
</v-tooltip>
4044
{{ item.receiver?.name || item.receiver?.id || item.name || '-' }}
4145
</template>
4246
<!-- Resource column (chantier D5+D8): fuses the former Type and
4347
Resource columns. The kind is the leading icon (Account /
4448
Group / Domain / File-tree); the text is the resource name
4549
(or DN for TREE delegates). Same lowercase-normalize as above. -->
4650
<template #item.name="{ item }">
47-
<v-icon size="small" class="me-1">{{ TYPE_ICONS[item.type?.toUpperCase()] || '' }}</v-icon>
51+
<v-tooltip :text="t('delegate.type.' + (item.type || '').toLowerCase())" location="top">
52+
<template #activator="{ props: tt }">
53+
<v-icon v-bind="tt" size="small" class="me-1">{{ TYPE_ICONS[item.type?.toUpperCase()] || '' }}</v-icon>
54+
</template>
55+
</v-tooltip>
4856
{{ item.name || '-' }}
4957
</template>
5058
<template #item.canAdmin="{ item }">
51-
<v-icon v-if="item.canAdmin" color="success" size="small">mdi-check</v-icon>
59+
<div class="text-center">
60+
<v-tooltip v-if="item.canAdmin" :text="t('delegate.adminGranted')" location="top">
61+
<template #activator="{ props: tt }">
62+
<v-icon v-bind="tt" color="success" size="small">mdi-check</v-icon>
63+
</template>
64+
</v-tooltip>
65+
</div>
5266
</template>
5367
<template #item.canWrite="{ item }">
54-
<v-icon v-if="item.canWrite" color="success" size="small">mdi-check</v-icon>
68+
<div class="text-center">
69+
<v-tooltip v-if="item.canWrite" :text="t('delegate.writeGranted')" location="top">
70+
<template #activator="{ props: tt }">
71+
<v-icon v-bind="tt" color="success" size="small">mdi-check</v-icon>
72+
</template>
73+
</v-tooltip>
74+
</div>
5575
</template>
5676
<template #item.actions="{ item }">
5777
<v-btn icon size="small" variant="text" @click.stop="openDialog(item.id)">
5878
<v-icon size="small">mdi-pencil</v-icon>
79+
<v-tooltip activator="parent" :text="t('common.edit')" location="top" />
5980
</v-btn>
6081
<v-btn icon size="small" variant="text" color="error" @click.stop="startDelete(item)">
6182
<v-icon size="small">mdi-delete</v-icon>
@@ -133,8 +154,8 @@ const editDelegateId = ref(null)
133154
const headers = computed(() => [
134155
{ title: t('delegate.receiver'), key: 'receiver', sortable: true },
135156
{ title: t('delegate.resource'), key: 'name', sortable: false },
136-
{ title: t('delegate.admin'), key: 'canAdmin', sortable: false, width: '80px', tooltip: t('delegate.adminHelp') },
137-
{ title: t('delegate.write'), key: 'canWrite', sortable: false, width: '80px', tooltip: t('delegate.writeHelp') },
157+
{ title: t('delegate.admin'), key: 'canAdmin', sortable: false, width: '80px', align: 'center', tooltip: t('delegate.adminHelp') },
158+
{ title: t('delegate.write'), key: 'canWrite', sortable: false, width: '80px', align: 'center', tooltip: t('delegate.writeHelp') },
138159
{ title: '', key: 'actions', sortable: false, width: '120px', align: 'center' },
139160
])
140161

ui/src/views/GroupEditView.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<v-card-text>
1111
<v-form ref="formRef" @submit.prevent="save">
1212
<v-text-field v-model="form.name" :label="t('common.name')" :rules="[rules.required]" :disabled="isEdit" variant="outlined" class="mb-2" />
13-
<v-autocomplete v-model="form.scope" :label="t('group.scope')" :items="availableScopes" :loading="scopesLoading" clearable variant="outlined" class="mb-2" />
13+
<v-autocomplete v-model="form.scope" :label="t('group.scope')" :items="availableScopes" :loading="scopesLoading" clearable variant="outlined" class="mb-2" autocomplete="off" />
1414
<!-- Parent group: lazy server-backed autosuggest. No groups are
1515
loaded until the dropdown opens (chantier H, Fabrice review) —
1616
the former mount-time bulk GET doesn't scale to 100k+ groups. -->
@@ -28,6 +28,7 @@
2828
class="mb-2"
2929
no-filter
3030
clearable
31+
autocomplete="off"
3132
@update:menu="onParentMenu"
3233
@update:search="onParentSearch"
3334
>

ui/src/views/GroupListView.vue

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,22 @@
3434
v-model:items-per-page="itemsPerPage" :headers="headers" :items="dt.items.value" :items-length="dt.totalItems.value" :loading="dt.loading.value" item-value="name" show-select hover
3535
@update:options="loadData" @click:row="(_, { item }) => router.push('/id/group/' + item.name)">
3636
<template #item.locked="{ item }">
37-
<v-icon v-if="item.locked" color="error" size="small">mdi-lock</v-icon>
37+
<div class="text-center">
38+
<v-tooltip v-if="item.locked" :text="t('user.statusLocked')" location="top">
39+
<template #activator="{ props: tt }">
40+
<v-icon v-bind="tt" color="error" size="small">mdi-lock</v-icon>
41+
</template>
42+
</v-tooltip>
43+
</div>
3844
</template>
3945
<template #item.actions="{ item }">
4046
<v-btn icon size="small" variant="text" @click.stop="router.push('/id/group/' + item.name)">
4147
<v-icon size="small">mdi-pencil</v-icon>
48+
<v-tooltip activator="parent" :text="t('common.edit')" location="top" />
4249
</v-btn>
4350
<v-btn icon size="small" variant="text" color="error" @click.stop="startDelete(item)">
4451
<v-icon size="small">mdi-delete</v-icon>
52+
<v-tooltip activator="parent" :text="t('common.delete')" location="top" />
4553
</v-btn>
4654
</template>
4755
</LigojDataTableServer>
@@ -107,8 +115,8 @@ let lastOptions = {}
107115
const headers = computed(() => [
108116
{ title: t('common.name'), key: 'name', sortable: true },
109117
{ title: t('group.scope'), key: 'scope', sortable: false },
110-
{ title: t('group.members'), key: 'count', sortable: false, width: '100px' },
111-
{ title: t('group.locked'), key: 'locked', sortable: false, width: '80px' },
118+
{ title: t('group.members'), key: 'count', sortable: false, width: '100px', align: 'center' },
119+
{ title: t('group.locked'), key: 'locked', sortable: false, width: '80px', align: 'center' },
112120
{ title: '', key: 'actions', sortable: false, width: '120px', align: 'center' },
113121
])
114122

ui/src/views/GroupMembersView.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<v-card variant="tonal" class="mb-4">
2828
<v-card-text class="d-flex flex-wrap align-center ga-2">
2929
<v-autocomplete v-model="newMember" v-model:search="searchTerm" :label="t('id.group.addPlaceholder')" :items="searchResults" item-title="label" item-value="id" :loading="searching" no-filter
30-
clearable return-object="false" variant="outlined" density="compact" hide-details style="min-width: 320px; flex: 1 1 320px" @update:search="onSearch" @update:menu="onSearchMenu" />
30+
clearable return-object="false" variant="outlined" density="compact" hide-details autocomplete="off" style="min-width: 320px; flex: 1 1 320px" @update:search="onSearch" @update:menu="onSearchMenu" />
3131
<v-btn color="primary" prepend-icon="mdi-account-plus" :disabled="!newMember || !groupName" :loading="adding" @click="addMember">
3232
{{ t('id.group.add') }}
3333
</v-btn>
@@ -61,6 +61,7 @@
6161
<template #item.actions="{ item }">
6262
<v-btn v-if="canRemove(item)" icon size="small" variant="text" color="error" :title="t('id.group.removeTitle')" @click.stop="startRemove(item)">
6363
<v-icon size="small">mdi-account-minus</v-icon>
64+
<v-tooltip activator="parent" :text="t('id.group.removeTitle')" location="top" />
6465
</v-btn>
6566
<v-tooltip v-else-if="isTransitive(item)" :text="t('id.group.transitive')" location="top">
6667
<template #activator="{ props: tt }">

ui/src/views/UserEditDialog.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
user types (300 ms debounced). v-model stores the company name
2727
as a string, matching the payload contract of rest/service/id/user. -->
2828
<v-autocomplete v-model="form.company" :items="companyResults" :loading="companyLoading" :search="companySearchQuery" item-title="name" item-value="name" :label="t('user.company')"
29-
placeholder="Rechercher une entité…" variant="outlined" class="mb-2" no-filter clearable @update:search="onCompanySearch">
29+
placeholder="Rechercher une entité…" variant="outlined" class="mb-2" no-filter clearable autocomplete="off" @update:search="onCompanySearch">
3030
<template #item="{ props: itemProps, item }">
3131
<v-list-item v-bind="itemProps" :title="item?.name || ''">
3232
<template v-if="item?.scope || item?.count !== undefined" #subtitle>
@@ -47,13 +47,13 @@
4747
multiple + chips lets the user type any email (no
4848
autocomplete source) and confirm with Enter or Tab;
4949
existing emails are restored as chips at load time. -->
50-
<v-combobox v-model="form.mails" :label="t('user.emails')" multiple chips closable-chips variant="outlined" class="mb-2" :hint="t('user.emailsHint')" persistent-hint />
50+
<v-combobox v-model="form.mails" :label="t('user.emails')" multiple chips closable-chips variant="outlined" class="mb-2" :hint="t('user.emailsHint')" persistent-hint autocomplete="off" />
5151
<!-- Auto-suggest for groups (multi-select). Queries
5252
rest/service/id/group as the user types (300 ms debounced).
5353
v-model holds an array of group **names** (strings),
5454
matching the payload contract of rest/service/id/user. -->
5555
<v-autocomplete v-model="groups" v-model:menu="groupMenu" :items="groupResults" :loading="groupLoading" :search="groupSearchQuery" item-title="name" item-value="name"
56-
:label="t('user.groups')" placeholder="Ajouter un groupe…" variant="outlined" class="mb-2" multiple chips closable-chips no-filter clearable @update:search="onGroupSearch"
56+
:label="t('user.groups')" placeholder="Ajouter un groupe…" variant="outlined" class="mb-2" multiple chips closable-chips no-filter clearable autocomplete="off" @update:search="onGroupSearch"
5757
@update:model-value="onGroupModelUpdate">
5858
<template #item="{ props: itemProps, item }">
5959
<v-list-item v-bind="itemProps" :title="item?.name || ''" />

0 commit comments

Comments
 (0)