Skip to content

Commit f12f21c

Browse files
committed
refactor(settings): migrate user dialogs to script setup
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
1 parent 94d26c1 commit f12f21c

9 files changed

Lines changed: 328 additions & 289 deletions

File tree

apps/settings/src/components/Users/EditUserDialog.vue

Lines changed: 84 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -41,120 +41,105 @@
4141
</NcDialog>
4242
</template>
4343

44-
<script>
44+
<script setup lang="ts">
45+
import type { IUser } from '../../views/user-types.d.ts'
46+
import type { QuotaOption } from './userFormUtils.ts'
47+
4548
import { showError, showSuccess } from '@nextcloud/dialogs'
49+
import { translate as t } from '@nextcloud/l10n'
4650
import { confirmPassword } from '@nextcloud/password-confirmation'
51+
import { computed, provide, reactive, ref } from 'vue'
4752
import NcButton from '@nextcloud/vue/components/NcButton'
4853
import NcDialog from '@nextcloud/vue/components/NcDialog'
4954
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
5055
import UserFormFields from './UserFormFields.vue'
5156
import logger from '../../logger.ts'
57+
import { useStore } from '../../store/index.js'
58+
import { formDataKey } from './injectionKeys.ts'
5259
import { diffPayload, userToFormData } from './userFormUtils.ts'
5360
54-
export default {
55-
name: 'EditUserDialog',
56-
57-
components: {
58-
NcButton,
59-
NcDialog,
60-
NcLoadingIcon,
61-
UserFormFields,
61+
const props = defineProps<{
62+
/** The user being edited */
63+
user: IUser
64+
/** Quota preset options for the quota select */
65+
quotaOptions: QuotaOption[]
66+
}>()
67+
68+
const emit = defineEmits<{
69+
closing: []
70+
}>()
71+
72+
const store = useStore()
73+
74+
const allGroups = store.getters.getGroups
75+
const serverLanguages = store.getters.getServerData.languages
76+
const formData = userToFormData(props.user, allGroups, props.quotaOptions, serverLanguages)
77+
78+
/** Snapshot of initial state for diffing */
79+
const initialData = structuredClone(formData)
80+
// Children inject this reactive object and mutate its properties via v-model.
81+
// Do not reassign editedUser entirely, the injected reference would go stale.
82+
const editedUser = reactive(formData)
83+
const saving = ref(false)
84+
const fieldErrors = ref<Record<string, string>>({})
85+
86+
// Children inject editedUser and mutate its properties via v-model.
87+
provide(formDataKey, editedUser)
88+
89+
const settings = computed(() => store.getters.getServerData)
90+
91+
const fieldConfig = computed(() => ({
92+
username: {
93+
show: true,
94+
disabled: true,
95+
label: t('settings', 'Account name'),
6296
},
6397
64-
// Children inject this reactive object and mutate its properties via v-model.
65-
// Do not reassign editedUser entirely, the injected reference would go stale.
66-
provide() {
67-
return {
68-
formData: this.editedUser,
69-
}
98+
password: {
99+
show: settings.value.canChangePassword && props.user.backendCapabilities.setPassword,
100+
label: t('settings', 'New password'),
70101
},
102+
}))
103+
104+
/**
105+
* Diff the form against its initial snapshot and submit only changed fields.
106+
* Maps a 422 response to per-field errors; closes the dialog on success or no-op.
107+
*/
108+
async function save() {
109+
// Guard against re-submit while a request is already running. The
110+
// button is only aria-disabled (not disabled), so it can still fire.
111+
if (saving.value) {
112+
return
113+
}
114+
fieldErrors.value = {}
71115
72-
props: {
73-
user: {
74-
type: Object,
75-
required: true,
76-
},
77-
78-
quotaOptions: {
79-
type: Array,
80-
required: true,
81-
},
82-
},
116+
const payload = diffPayload(initialData, editedUser)
117+
if (Object.keys(payload).length === 0) {
118+
emit('closing')
119+
return
120+
}
83121
84-
emits: ['closing'],
85-
86-
data() {
87-
const allGroups = this.$store.getters.getGroups
88-
const serverLanguages = this.$store.getters.getServerData.languages
89-
const formData = userToFormData(this.user, allGroups, this.quotaOptions, serverLanguages)
90-
return {
91-
/** Snapshot of initial state for diffing */
92-
initialData: structuredClone(formData),
93-
/** Mutable form state */
94-
editedUser: formData,
95-
saving: false,
96-
fieldErrors: {},
122+
saving.value = true
123+
try {
124+
await confirmPassword()
125+
await store.dispatch('editUserMultiField', {
126+
userid: props.user.id,
127+
payload,
128+
})
129+
showSuccess(t('settings', 'Account updated'))
130+
emit('closing')
131+
} catch (error) {
132+
const errors = (error as { response?: { data?: { ocs?: { data?: { errors?: Record<string, string> } } } } })
133+
.response?.data?.ocs?.data?.errors
134+
if (errors && typeof errors === 'object') {
135+
fieldErrors.value = errors
136+
} else {
137+
logger.error('Failed to update account', { error })
138+
showError(t('settings', 'Failed to update account'))
97139
}
98-
},
99-
100-
computed: {
101-
settings() {
102-
return this.$store.getters.getServerData
103-
},
104-
105-
fieldConfig() {
106-
return {
107-
username: {
108-
show: true,
109-
disabled: true,
110-
label: t('settings', 'Account name'),
111-
},
112-
113-
password: {
114-
show: this.settings.canChangePassword && this.user.backendCapabilities.setPassword,
115-
label: t('settings', 'New password'),
116-
},
117-
}
118-
},
119-
},
120-
121-
methods: {
122-
async save() {
123-
// Guard against re-submit while a request is already running. The
124-
// button is only aria-disabled (not disabled), so it can still fire.
125-
if (this.saving) {
126-
return
127-
}
128-
this.fieldErrors = {}
129-
130-
const payload = diffPayload(this.initialData, this.editedUser)
131-
if (Object.keys(payload).length === 0) {
132-
this.$emit('closing')
133-
return
134-
}
135-
136-
this.saving = true
137-
try {
138-
await confirmPassword()
139-
await this.$store.dispatch('editUserMultiField', {
140-
userid: this.user.id,
141-
payload,
142-
})
143-
showSuccess(t('settings', 'Account updated'))
144-
this.$emit('closing')
145-
} catch (error) {
146-
const errors = error.response?.data?.ocs?.data?.errors
147-
if (errors && typeof errors === 'object') {
148-
this.fieldErrors = errors
149-
} else {
150-
logger.error('Failed to update account', { error })
151-
showError(t('settings', 'Failed to update account'))
152-
}
153-
} finally {
154-
this.saving = false
155-
}
156-
},
157-
},
140+
} finally {
141+
saving.value = false
142+
}
158143
}
159144
</script>
160145

0 commit comments

Comments
 (0)