diff options
Diffstat (limited to 'apps/dav/src/components')
-rw-r--r-- | apps/dav/src/components/AbsenceForm.vue | 121 |
1 files changed, 119 insertions, 2 deletions
diff --git a/apps/dav/src/components/AbsenceForm.vue b/apps/dav/src/components/AbsenceForm.vue index fa62c56039d..33f1483a7fb 100644 --- a/apps/dav/src/components/AbsenceForm.vue +++ b/apps/dav/src/components/AbsenceForm.vue @@ -17,6 +17,21 @@ class="absence__dates__picker" :required="true" /> </div> + <label for="replacement-search-input">{{ $t('dav', 'Out of office replacement (optional)') }}</label> + <NcSelect ref="select" + v-model="replacementUser" + input-id="replacement-search-input" + :loading="searchLoading" + :placeholder="$t('dav', 'Name of the replacement')" + :clear-search-on-blur="() => false" + :user-select="true" + :options="options" + @search="asyncFind" + > + <template #no-options="{ search }"> + {{ search ?$t('dav', 'No results.') : $t('dav', 'Start typing.') }} + </template> + </NcSelect> <NcTextField :value.sync="status" :label="$t('dav', 'Short absence status')" :required="true" /> <NcTextArea :value.sync="message" :label="$t('dav', 'Long absence Message')" :required="true" /> @@ -39,13 +54,16 @@ import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' import NcTextArea from '@nextcloud/vue/dist/Components/NcTextArea.js' +import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js' import NcDateTimePickerNative from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js' import { generateOcsUrl } from '@nextcloud/router' import { getCurrentUser } from '@nextcloud/auth' +import debounce from 'debounce' import axios from '@nextcloud/axios' import { formatDateAsYMD } from '../utils/date.js' import { loadState } from '@nextcloud/initial-state' import { showError, showSuccess } from '@nextcloud/dialogs' +import { Type as ShareTypes } from '@nextcloud/sharing' import logger from '../service/logger.js' @@ -56,16 +74,20 @@ export default { NcTextField, NcTextArea, NcDateTimePickerNative, + NcSelect }, data() { - const { firstDay, lastDay, status, message } = loadState('dav', 'absence', {}) - + const { firstDay, lastDay, status, message ,replacementUserId ,replacementUserDisplayName } = loadState('dav', 'absence', {}) return { loading: false, status: status ?? '', message: message ?? '', firstDay: firstDay ? new Date(firstDay) : new Date(), lastDay: lastDay ? new Date(lastDay) : null, + replacementUserId: replacementUserId , + replacementUser: replacementUserId ? { user: replacementUserId, displayName: replacementUserDisplayName } : null, + searchLoading: false, + options: [], } }, computed: { @@ -93,6 +115,99 @@ export default { this.firstDay = new Date() this.lastDay = null }, + + /** + * Format shares for the multiselect options + * + * @param {object} result select entry item + * @return {object} + */ + formatForMultiselect(result) { + return { + user: result.uuid || result.value.shareWith, + displayName: result.name || result.label, + subtitle: result.dsc | '' + } + }, + + async asyncFind(query) { + this.searchLoading = true + await this.debounceGetSuggestions(query.trim()) + }, + /** + * Get suggestions + * + * @param {string} search the search query + */ + async getSuggestions(search) { + + const shareType = [ + ShareTypes.SHARE_TYPE_USER, + ] + + let request = null + try { + request = await axios.get(generateOcsUrl('apps/files_sharing/api/v1/sharees'), { + params: { + format: 'json', + itemType: 'file', + search, + shareType, + }, + }) + } catch (error) { + console.error('Error fetching suggestions', error) + return + } + + const data = request.data.ocs.data + const exact = request.data.ocs.data.exact + data.exact = [] // removing exact from general results + const rawExactSuggestions = exact.users + const rawSuggestions = data.users + console.info('rawExactSuggestions', rawExactSuggestions) + console.info('rawSuggestions', rawSuggestions) + // remove invalid data and format to user-select layout + const exactSuggestions = rawExactSuggestions + .map(share => this.formatForMultiselect(share)) + const suggestions = rawSuggestions + .map(share => this.formatForMultiselect(share)) + + const allSuggestions = exactSuggestions.concat(suggestions) + + // Count occurrences of display names in order to provide a distinguishable description if needed + const nameCounts = allSuggestions.reduce((nameCounts, result) => { + if (!result.displayName) { + return nameCounts + } + if (!nameCounts[result.displayName]) { + nameCounts[result.displayName] = 0 + } + nameCounts[result.displayName]++ + return nameCounts + }, {}) + + this.options = allSuggestions.map(item => { + // Make sure that items with duplicate displayName get the shareWith applied as a description + if (nameCounts[item.displayName] > 1 && !item.desc) { + return { ...item, desc: item.shareWithDisplayNameUnique } + } + return item + }) + + this.searchLoading = false + console.info('suggestions', this.options) + }, + + /** + * Debounce getSuggestions + * + * @param {...*} args the arguments + */ + debounceGetSuggestions: debounce(function(...args) { + this.getSuggestions(...args) + }, 300), + async saveForm() { if (!this.valid) { return @@ -105,6 +220,8 @@ export default { lastDay: formatDateAsYMD(this.lastDay), status: this.status, message: this.message, + replacementUserId: this.replacementUser?.user ?? null, + replacementUserDisplayName: this.replacementUser?.displayName ?? null, }) showSuccess(this.$t('dav', 'Absence saved')) } catch (error) { |