aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/src/components/PersonalInfo/shared/AccountPropertySection.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/src/components/PersonalInfo/shared/AccountPropertySection.vue')
-rw-r--r--apps/settings/src/components/PersonalInfo/shared/AccountPropertySection.vue243
1 files changed, 243 insertions, 0 deletions
diff --git a/apps/settings/src/components/PersonalInfo/shared/AccountPropertySection.vue b/apps/settings/src/components/PersonalInfo/shared/AccountPropertySection.vue
new file mode 100644
index 00000000000..d039641ec72
--- /dev/null
+++ b/apps/settings/src/components/PersonalInfo/shared/AccountPropertySection.vue
@@ -0,0 +1,243 @@
+<!--
+ - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+<template>
+ <section>
+ <HeaderBar :scope="scope"
+ :readable="readable"
+ :input-id="inputId"
+ :is-editable="isEditable"
+ @update:scope="(scope) => $emit('update:scope', scope)" />
+
+ <div v-if="isEditable" class="property">
+ <NcTextArea v-if="multiLine"
+ :id="inputId"
+ autocapitalize="none"
+ autocomplete="off"
+ :error="hasError || !!helperText"
+ :helper-text="helperText"
+ label-outside
+ :placeholder="placeholder"
+ rows="8"
+ spellcheck="false"
+ :success="isSuccess"
+ :value.sync="inputValue" />
+ <NcInputField v-else
+ :id="inputId"
+ ref="input"
+ autocapitalize="none"
+ :autocomplete="autocomplete"
+ :error="hasError || !!helperText"
+ :helper-text="helperText"
+ label-outside
+ :placeholder="placeholder"
+ spellcheck="false"
+ :success="isSuccess"
+ :type="type"
+ :value.sync="inputValue" />
+ </div>
+ <span v-else>
+ {{ value || t('settings', 'No {property} set', { property: readable.toLocaleLowerCase() }) }}
+ </span>
+ </section>
+</template>
+
+<script>
+import debounce from 'debounce'
+import NcInputField from '@nextcloud/vue/components/NcInputField'
+import NcTextArea from '@nextcloud/vue/components/NcTextArea'
+
+import HeaderBar from './HeaderBar.vue'
+
+import { savePrimaryAccountProperty } from '../../../service/PersonalInfo/PersonalInfoService.js'
+import { handleError } from '../../../utils/handlers.ts'
+
+export default {
+ name: 'AccountPropertySection',
+
+ components: {
+ HeaderBar,
+ NcInputField,
+ NcTextArea,
+ },
+
+ props: {
+ name: {
+ type: String,
+ required: true,
+ },
+ value: {
+ type: String,
+ required: true,
+ },
+ scope: {
+ type: String,
+ required: true,
+ },
+ readable: {
+ type: String,
+ required: true,
+ },
+ placeholder: {
+ type: String,
+ required: true,
+ },
+ type: {
+ type: String,
+ default: 'text',
+ },
+ isEditable: {
+ type: Boolean,
+ default: true,
+ },
+ multiLine: {
+ type: Boolean,
+ default: false,
+ },
+ onValidate: {
+ type: Function,
+ default: null,
+ },
+ onSave: {
+ type: Function,
+ default: null,
+ },
+ autocomplete: {
+ type: String,
+ default: null,
+ },
+ },
+
+ emits: ['update:scope', 'update:value'],
+
+ data() {
+ return {
+ initialValue: this.value,
+ helperText: '',
+ isSuccess: false,
+ hasError: false,
+ }
+ },
+
+ computed: {
+ inputId() {
+ return `account-property-${this.name}`
+ },
+
+ inputValue: {
+ get() {
+ return this.value
+ },
+ set(value) {
+ this.$emit('update:value', value)
+ this.debouncePropertyChange(value.trim())
+ },
+ },
+
+ debouncePropertyChange() {
+ return debounce(async function(value) {
+ this.helperText = this.$refs.input?.$refs.input?.validationMessage || ''
+ if (this.helperText !== '') {
+ return
+ }
+ this.hasError = this.onValidate && !this.onValidate(value)
+ if (this.hasError) {
+ this.helperText = t('settings', 'Invalid value')
+ return
+ }
+ await this.updateProperty(value)
+ }, 1000)
+ },
+ },
+
+ methods: {
+ async updateProperty(value) {
+ try {
+ this.hasError = false
+ const responseData = await savePrimaryAccountProperty(
+ this.name,
+ value,
+ )
+ this.handleResponse({
+ value,
+ status: responseData.ocs?.meta?.status,
+ })
+ } catch (e) {
+ this.handleResponse({
+ errorMessage: t('settings', 'Unable to update {property}', { property: this.readable.toLocaleLowerCase() }),
+ error: e,
+ })
+ }
+ },
+
+ handleResponse({ value, status, errorMessage, error }) {
+ if (status === 'ok') {
+ this.initialValue = value
+ if (this.onSave) {
+ this.onSave(value)
+ }
+ this.isSuccess = true
+ setTimeout(() => { this.isSuccess = false }, 2000)
+ } else {
+ handleError(error, errorMessage)
+ this.hasError = true
+ }
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+section {
+ padding: 10px 10px;
+
+ .property {
+ display: flex;
+ flex-direction: row;
+ align-items: start;
+ gap: 4px;
+
+ .property__actions-container {
+ margin-top: 6px;
+ justify-self: flex-end;
+ align-self: flex-end;
+
+ display: flex;
+ gap: 0 2px;
+ margin-inline-end: 5px;
+ margin-bottom: 5px;
+ }
+ }
+
+ .property__helper-text-message {
+ padding: 4px 0;
+ display: flex;
+ align-items: center;
+
+ &__icon {
+ margin-inline-end: 8px;
+ align-self: start;
+ margin-top: 4px;
+ }
+
+ &--error {
+ color: var(--color-error);
+ }
+ }
+
+ .fade-enter,
+ .fade-leave-to {
+ opacity: 0;
+ }
+
+ .fade-enter-active {
+ transition: opacity 200ms ease-out;
+ }
+
+ .fade-leave-active {
+ transition: opacity 300ms ease-out;
+ }
+}
+</style>