Skip to content

Roles picker enhancements #2

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/resources/locales/en_US/menu.po
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ msgstr "Toggle menu collapse button"
msgid "options"
msgstr "Options"

msgid "person.canvas"
msgstr "Person Canvas"

msgid "registries"
msgstr "Available {0} Registries"

Expand Down
3 changes: 3 additions & 0 deletions app/resources/locales/en_US/operation.po
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ msgstr "show more"
msgid "autocomplete.people.desc"
msgstr "Begin typing to find a person (use at least {0} characters from a name, email address, or identifier)"

msgid "autocomplete.people.field.desc"
msgstr "Begin typing to find a person (use characters from a name, email address, or identifier)"

msgid "autocomplete.people.label"
msgstr "Search for a person"

Expand Down
8 changes: 8 additions & 0 deletions app/src/View/Helper/VueHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,25 @@ class VueHelper extends Helper {
'report.for',
'value.copied',
],
'menu' => [
'person.canvas'
],
'operation' => [
'add',
'add.member',
'add.owner',
'autocomplete.pager.show.more',
'autocomplete.people.desc',
'autocomplete.people.field.desc',
'autocomplete.people.label',
'autocomplete.people.placeholder',
'close',
'copy',
'copy.value',
'edit',
'primary',
'remove',
'view',
'visit.link',
],
'result' => [
Expand Down
5 changes: 1 addition & 4 deletions app/templates/element/form/infoDiv/autocomplete.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,4 @@
formParams: $formParams ?? []
);
?>
<div class="field-desc field-autocomplete-desc">
<span class="material-symbols-outlined">info</span>
<span><?= __d('operation','autocomplete.people.desc',['2']) ?></span>
</div>

5 changes: 4 additions & 1 deletion app/templates/element/peopleAutocomplete.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@
},
formParams: <?= json_encode($formParams ?? []) ?>,
},
error: ''
error: '',
core: {
webroot: '<?= $this->request->getAttribute('webroot') ?>'
}
}
},
components: {
Expand Down
10 changes: 10 additions & 0 deletions app/webroot/css/co-base.css
Original file line number Diff line number Diff line change
Expand Up @@ -1821,6 +1821,16 @@ ul.form-list .cm-time-picker-vals li {
text-decoration: underline;
background-color: var(--cmg-color-bg-002);
}
.cm-ac-input-group > div {
flex-grow: 1;
}
.cm-ac-link-to-person {
gap: 0.25em;
color: var(--cmg-color-link);
}
#main .cm-ac-link-to-person:hover {
text-decoration: none;
}
.item-with-type {
display: grid;
grid-template-columns: 1fr 1fr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default {
people: [],
rawData: [],
person: '',
personUrl: '',
identifierType: {},
emailType: {},
loading: false,
Expand Down Expand Up @@ -193,7 +194,7 @@ export default {
},
setPerson() {
if(['default', 'field'].includes(this.options.type)) {
this.options.inputProps.dataPersonid = this.person.value
this.options.inputProps.dataPersonid = this.person.value;
} else {
// The picker is stand-alone, and should render the configured page in a modal on @item-select
const urlForModal = this.options.actionUrl + '&person_id=' + this.person.value;
Expand Down Expand Up @@ -248,7 +249,8 @@ export default {
if(this.options.inputValue != undefined
&& this.options.inputValue != ''
&& this.options.inputProps.name.endsWith('person_id')) {
this.person = `${this.options.formParams?.fullName} (ID: ${this.options.inputValue})`
this.person = `${this.options.formParams?.fullName} (ID: ${this.options.inputValue})`;
this.personUrl = `${this.core.webroot}people/edit/${this.options.inputValue}`;
}
},
computed: {
Expand Down Expand Up @@ -284,75 +286,86 @@ export default {
}
},
template: `
<label v-if="hasAutoCompleteLabel" class="mr-2" :for="this.options.htmlId">{{ this.autoCompleteLabel }}</label>
<MiniLoader :isLoading="loading" :classes="getMiniLoaderClasses"/>
<AutoComplete
v-model="person"
inputClass="cm-autocomplete"
:inputId="this.options.htmlId"
:inputProps="this.options.inputProps"
:placeholder="this.txt['operation.autocomplete.people.placeholder']"
panelClass="cm-autocomplete-panel"
optionLabel="label"
optionDisabled="isMember"
:minLength="this.options.minLength"
:delay="500"
loadingIcon=null
:suggestions="this.people"
forceSelection
@complete="searchPeople"
@show="calculateDisabled"
@keyup.arrow-down="onListNavigate"
@keyup.arrow-up="onListNavigate"
@item-select="setPerson">
<template #option="slotProps">
<div class="cm-ac-item">
<div class="cm-ac-item-primary">
<div class="cm-ac-name">
<!-- XXX The input field will be updated with the option.label value. Here we only need the full name -->
<span class="cm-ac-name-value" v-if="slotProps.option.isMember" v-html="slotProps.option.fullName"></span>
<span class="cm-ac-name-value" v-else v-html="this.highlightedquery(slotProps.option.fullName, query)"></span>
<span class="mr-1 badge bg-success" v-if="slotProps.option.isMember">{{ this.txt['controller.GroupMembers'] }}</span>
<label v-if="hasAutoCompleteLabel" class="mr-2" :for="this.options.htmlId">{{ this.autoCompleteLabel }}</label>
<MiniLoader :isLoading="loading" :classes="getMiniLoaderClasses"/>
<div class="cm-ac-input-group input-group">
<AutoComplete
v-model="person"
inputClass="cm-autocomplete"
:inputId="this.options.htmlId"
:inputProps="this.options.inputProps"
:placeholder="this.txt['operation.autocomplete.people.placeholder']"
panelClass="cm-autocomplete-panel"
optionLabel="label"
optionDisabled="isMember"
:minLength="this.options.minLength"
:delay="500"
loadingIcon=null
:suggestions="this.people"
forceSelection
@complete="searchPeople"
@show="calculateDisabled"
@keyup.arrow-down="onListNavigate"
@keyup.arrow-up="onListNavigate"
@item-select="setPerson">
<template #option="slotProps">
<div class="cm-ac-item">
<div class="cm-ac-item-primary">
<div class="cm-ac-name">
<!-- XXX The input field will be updated with the option.label value. Here we only need the full name -->
<span class="cm-ac-name-value" v-if="slotProps.option.isMember" v-html="slotProps.option.fullName"></span>
<span class="cm-ac-name-value" v-else v-html="this.highlightedquery(slotProps.option.fullName, query)"></span>
<span class="mr-1 badge bg-success" v-if="slotProps.option.isMember">{{ this.txt['controller.GroupMembers'] }}</span>
</div>
<div class="cm-ac-item-id">
ID: {{ slotProps.option.itemId }}
</div>
</div>
<div class="cm-ac-subitems">
<div class="cm-ac-subitem cm-ac-email" v-if="slotProps.option.email">
<span class="cm-ac-label" v-if="slotProps.option.emailLabel">{{ slotProps.option.emailLabel }}</span>
<span class="cm-ac-value">
<ItemWithType
v-for="item in slotProps.option.email"
:item="item"
kind="email"
:query="query"
:highlightedquery="highlightedquery"
:isMember="slotProps.option.isMember"
/>
</span>
</div>
<div class="cm-ac-subitem cm-ac-id" v-if="slotProps.option.identifier">
<span class="cm-ac-label" v-if="slotProps.option.identifierLabel">{{ slotProps.option.identifierLabel }}</span>
<span class="cm-ac-value">
<ItemWithType
v-for="item in slotProps.option.identifier"
:query="query"
:item="item"
kind="identifier"
:highlightedquery="highlightedquery"
:isMember="slotProps.option.isMember"
/>
</span>
</div>
</div>
</div>
<div class="cm-ac-item-id">
ID: {{ slotProps.option.itemId }}
</template>
<template #footer="slotProps" v-if="hasMorePages">
<div class="cm-ac-pager">
<a href="#" @click="this.fetchMorePeople()">{{ this.txt['operation.autocomplete.pager.show.more'] }}</a>
</div>
</div>
<div class="cm-ac-subitems">
<div class="cm-ac-subitem cm-ac-email" v-if="slotProps.option.email">
<span class="cm-ac-label" v-if="slotProps.option.emailLabel">{{ slotProps.option.emailLabel }}</span>
<span class="cm-ac-value">
<ItemWithType
v-for="item in slotProps.option.email"
:item="item"
kind="email"
:query="query"
:highlightedquery="highlightedquery"
:isMember="slotProps.option.isMember"
/>
</span>
</div>
<div class="cm-ac-subitem cm-ac-id" v-if="slotProps.option.identifier">
<span class="cm-ac-label" v-if="slotProps.option.identifierLabel">{{ slotProps.option.identifierLabel }}</span>
<span class="cm-ac-value">
<ItemWithType
v-for="item in slotProps.option.identifier"
:query="query"
:item="item"
kind="identifier"
:highlightedquery="highlightedquery"
:isMember="slotProps.option.isMember"
/>
</span>
</div>
</div>
</div>
</template>
<template #footer="slotProps" v-if="hasMorePages">
<div class="cm-ac-pager">
<a href="#" @click="this.fetchMorePeople()">{{ this.txt['operation.autocomplete.pager.show.more'] }}</a>
</div>
</template>
</AutoComplete>
</template>
</AutoComplete>
<a v-if="this.person" :href="this.personUrl" class="input-group-text cm-ac-link-to-person">
<span class="material-symbols">person_check</span>
<span class="cm-ac-link-to-person-text">{{ this.txt['menu.person.canvas'] }}</span>
</a>
</div>
<div v-if="this.options.type == 'field'" class="field-desc field-autocomplete-desc">
<span class="material-symbols-outlined">info</span>
<span>{{ this.txt['operation.autocomplete.people.field.desc'] }}</span>
</div>
</div>
`
}