2023-07-26 04:09:10 +02:00
|
|
|
<script setup lang="ts">
|
2023-07-27 17:54:29 +02:00
|
|
|
import { useForm } from 'vee-validate'
|
|
|
|
import { toTypedSchema } from '@vee-validate/yup'
|
2023-07-26 04:09:10 +02:00
|
|
|
import * as yup from 'yup'
|
2023-08-28 19:15:59 +02:00
|
|
|
import { pick, exclude } from '@/utils/common'
|
2023-09-04 16:43:51 +02:00
|
|
|
import type { User } from '@/composables/states'
|
2023-07-26 04:09:10 +02:00
|
|
|
|
2023-11-18 17:08:22 +01:00
|
|
|
const { t, locale, locales } = useI18n()
|
2023-09-07 18:13:09 +02:00
|
|
|
|
|
|
|
useHead({
|
|
|
|
title: t('footerlink_edit'),
|
|
|
|
})
|
|
|
|
|
2023-09-04 16:43:51 +02:00
|
|
|
const user = await useUser()
|
2023-07-26 04:09:10 +02:00
|
|
|
|
2023-11-18 17:08:22 +01:00
|
|
|
// Browser
|
|
|
|
const localesAsOptions = computed(() => {
|
|
|
|
return locales.value.map((locale) => ({
|
|
|
|
text: locale.name,
|
|
|
|
value: locale.code,
|
|
|
|
}))
|
|
|
|
})
|
|
|
|
|
|
|
|
const colorMode = useColorMode()
|
|
|
|
const themesAsOptions = [
|
|
|
|
'system',
|
|
|
|
'light',
|
|
|
|
'dark',
|
|
|
|
'cupcake',
|
|
|
|
'bumblebee',
|
|
|
|
'emerald',
|
|
|
|
'corporate',
|
|
|
|
'synthwave',
|
|
|
|
'retro',
|
|
|
|
'cyberpunk',
|
|
|
|
'valentine',
|
|
|
|
'halloween',
|
|
|
|
'garden',
|
|
|
|
'forest',
|
|
|
|
'aqua',
|
|
|
|
'lofi',
|
|
|
|
'pastel',
|
|
|
|
'fantasy',
|
|
|
|
'wireframe',
|
|
|
|
'black',
|
|
|
|
'luxury',
|
|
|
|
'dracula',
|
|
|
|
'cmyk',
|
|
|
|
'autumn',
|
|
|
|
'business',
|
|
|
|
'acid',
|
|
|
|
'lemonade',
|
|
|
|
'night',
|
|
|
|
'coffee',
|
|
|
|
'winter',
|
|
|
|
'dim',
|
|
|
|
'nord',
|
|
|
|
'sunset',
|
|
|
|
].map((theme) => ({
|
|
|
|
text: theme.charAt(0).toUpperCase() + theme.slice(1),
|
|
|
|
value: theme,
|
|
|
|
}))
|
|
|
|
// Server
|
2023-08-28 15:10:25 +02:00
|
|
|
const loading: Ref<boolean | null> = ref(false)
|
|
|
|
const feedback: Ref<{
|
|
|
|
variant: 'success' | 'warning' | 'error'
|
|
|
|
icon: string
|
|
|
|
message: string
|
|
|
|
} | null> = ref(null)
|
|
|
|
|
|
|
|
const { handleSubmit, setFieldError, resetForm, meta } = useForm({
|
2023-07-27 17:54:29 +02:00
|
|
|
validationSchema: toTypedSchema(
|
|
|
|
yup.object({
|
2023-08-28 15:10:25 +02:00
|
|
|
// username: yup.string().required(),
|
2023-08-07 17:29:04 +02:00
|
|
|
fullname: yup.string().required().min(2),
|
2023-08-29 16:31:38 +02:00
|
|
|
currentpassword: yup
|
2023-08-28 19:15:59 +02:00
|
|
|
.string()
|
2023-08-29 16:31:38 +02:00
|
|
|
.when('newpassword', ([newpassword], schema) => {
|
|
|
|
return newpassword ? schema.required() : schema
|
2023-08-28 19:15:59 +02:00
|
|
|
}),
|
2023-08-29 16:31:38 +02:00
|
|
|
newpassword: yup.string().matches(/.{8,}/, {
|
2023-08-28 19:15:59 +02:00
|
|
|
excludeEmptyString: true,
|
|
|
|
message: { key: 'v.string_too_short', values: { min: 8 } },
|
|
|
|
}),
|
2023-08-29 16:31:38 +02:00
|
|
|
confirmpassword: yup
|
2023-08-28 19:15:59 +02:00
|
|
|
.string()
|
2023-08-29 16:31:38 +02:00
|
|
|
.when('newpassword', ([newpassword], schema) => {
|
|
|
|
return newpassword
|
|
|
|
? schema.oneOf([yup.ref('newpassword')], 'v.password_not_match')
|
2023-08-28 19:15:59 +02:00
|
|
|
: schema
|
|
|
|
}),
|
2023-08-28 15:10:25 +02:00
|
|
|
mailalias: yup.array().of(yup.string().email().required()).required(),
|
|
|
|
mailforward: yup.array().of(yup.string().email().required()).required(),
|
2023-07-27 17:54:29 +02:00
|
|
|
}),
|
|
|
|
),
|
|
|
|
initialValues: {
|
2023-08-29 16:31:38 +02:00
|
|
|
currentpassword: '',
|
|
|
|
newpassword: '',
|
|
|
|
confirmpassword: '',
|
2023-09-04 16:43:51 +02:00
|
|
|
...pick(user.value, 'fullname', 'mailalias', 'mailforward'),
|
2023-07-27 17:54:29 +02:00
|
|
|
},
|
|
|
|
})
|
2023-07-26 04:09:10 +02:00
|
|
|
|
2023-08-28 15:10:25 +02:00
|
|
|
watch(
|
2023-08-29 16:31:38 +02:00
|
|
|
() => meta.value.dirty,
|
2023-08-28 15:10:25 +02:00
|
|
|
(value) => {
|
|
|
|
// remove loading and feedback on edition
|
|
|
|
if (value) {
|
|
|
|
loading.value = null
|
|
|
|
feedback.value = null
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2023-08-01 15:13:35 +02:00
|
|
|
const onSubmit = handleSubmit(async (form) => {
|
2023-08-28 15:10:25 +02:00
|
|
|
loading.value = true
|
|
|
|
|
|
|
|
const { error, data } = await useApi<
|
2023-09-04 16:43:51 +02:00
|
|
|
Pick<User, 'fullname' | 'mailalias' | 'mailforward'>
|
2023-08-28 15:10:25 +02:00
|
|
|
>('/update', {
|
2023-08-01 15:13:35 +02:00
|
|
|
method: 'PUT',
|
2023-08-29 16:31:38 +02:00
|
|
|
body: exclude(form, 'confirmpassword'),
|
2023-08-01 15:13:35 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
if (error.value) {
|
2023-08-29 16:31:38 +02:00
|
|
|
// Reset form dirty state but keep previous values
|
|
|
|
resetForm({ values: form })
|
2023-08-28 15:10:25 +02:00
|
|
|
const errData = error.value.data
|
2023-11-07 18:45:21 +01:00
|
|
|
let message
|
|
|
|
|
2023-08-28 15:10:25 +02:00
|
|
|
if (errData.path) {
|
|
|
|
setFieldError(errData.path, errData.error)
|
2023-11-11 15:07:40 +01:00
|
|
|
message = t('form_has_errors')
|
2023-08-28 15:10:25 +02:00
|
|
|
} else {
|
2023-11-07 18:45:21 +01:00
|
|
|
message = errData.error || errData
|
|
|
|
}
|
|
|
|
feedback.value = {
|
|
|
|
variant: 'error',
|
2023-11-08 19:07:16 +01:00
|
|
|
icon: 'alert',
|
2023-11-07 18:45:21 +01:00
|
|
|
message,
|
2023-08-28 15:10:25 +02:00
|
|
|
}
|
2023-08-01 15:13:35 +02:00
|
|
|
} else if (data.value) {
|
2023-08-29 16:31:38 +02:00
|
|
|
// redirect on password change
|
|
|
|
if (form.newpassword) {
|
|
|
|
useIsLoggedIn().value = false
|
|
|
|
return navigateTo('/login')
|
|
|
|
}
|
|
|
|
|
2023-09-04 16:43:51 +02:00
|
|
|
Object.assign(user.value, data)
|
2023-08-28 15:10:25 +02:00
|
|
|
resetForm({
|
2023-08-28 19:15:59 +02:00
|
|
|
values: {
|
|
|
|
...data.value,
|
2023-08-29 16:31:38 +02:00
|
|
|
currentpassword: '',
|
|
|
|
newpassword: '',
|
|
|
|
confirmpassword: '',
|
2023-08-28 19:15:59 +02:00
|
|
|
},
|
2023-08-28 15:10:25 +02:00
|
|
|
})
|
|
|
|
feedback.value = {
|
|
|
|
variant: 'success',
|
2023-11-08 19:07:16 +01:00
|
|
|
icon: 'thumb-up',
|
2023-08-28 15:10:25 +02:00
|
|
|
message: t('user_profile_updated'),
|
|
|
|
}
|
2023-08-01 15:13:35 +02:00
|
|
|
}
|
2023-08-28 15:10:25 +02:00
|
|
|
loading.value = false
|
2023-07-27 17:54:29 +02:00
|
|
|
})
|
2023-07-26 04:09:10 +02:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2023-08-06 16:27:24 +02:00
|
|
|
<div>
|
|
|
|
<PageTitle :text="$t('footerlink_edit')" />
|
2023-07-26 04:09:10 +02:00
|
|
|
|
2023-11-18 17:08:22 +01:00
|
|
|
<section class="mt-5">
|
|
|
|
<h2 class="text-3xl">{{ t('edit_browser_settings') }}</h2>
|
|
|
|
|
|
|
|
<form novalidate class="my-10" @submit.prevent>
|
|
|
|
<div role="group" class="flex align mb-3">
|
|
|
|
<!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->
|
|
|
|
<label for="language" class="label me-3">{{ t('language') }}</label>
|
|
|
|
<select id="language" v-model="locale" class="select select-bordered">
|
|
|
|
<option disabled selected>{{ t('language') }}</option>
|
|
|
|
<option
|
|
|
|
v-for="option in localesAsOptions"
|
|
|
|
:key="option.value"
|
|
|
|
:value="option.value"
|
|
|
|
>
|
|
|
|
{{ option.text }}
|
|
|
|
</option>
|
|
|
|
</select>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div role="group" class="flex align">
|
|
|
|
<!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->
|
|
|
|
<label for="theme" class="label me-3">{{ t('theme') }}</label>
|
|
|
|
<select
|
|
|
|
id="theme"
|
|
|
|
v-model="colorMode.preference"
|
|
|
|
class="select select-bordered"
|
|
|
|
>
|
|
|
|
<option disabled selected>{{ t('theme') }}</option>
|
|
|
|
<option
|
|
|
|
v-for="option in themesAsOptions"
|
|
|
|
:key="option.value"
|
|
|
|
:value="option.value"
|
|
|
|
>
|
|
|
|
{{ option.text }}
|
|
|
|
</option>
|
|
|
|
</select>
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<h2 class="text-3xl">{{ t('edit_personal_settings') }}</h2>
|
|
|
|
|
|
|
|
<form novalidate class="my-10" @submit="onSubmit">
|
|
|
|
<div class="lg:flex lg:justify-between">
|
|
|
|
<div class="lg:w-1/2 lg:me-20">
|
|
|
|
<!-- <FormField name="username" :label="$t('username')" class="mb-3">
|
2023-08-06 16:27:24 +02:00
|
|
|
<TextInput
|
|
|
|
name="username"
|
|
|
|
type="text"
|
|
|
|
:placeholder="$t('username')"
|
|
|
|
disabled
|
|
|
|
class="w-full"
|
|
|
|
/>
|
2023-08-28 15:10:25 +02:00
|
|
|
</FormField> -->
|
2023-08-06 16:27:24 +02:00
|
|
|
|
2023-11-18 17:08:22 +01:00
|
|
|
<FormField name="fullname" :label="$t('fullname')" class="mb-10">
|
|
|
|
<TextInput
|
|
|
|
name="fullname"
|
|
|
|
type="text"
|
|
|
|
:placeholder="$t('fullname')"
|
|
|
|
autocomplete="name"
|
|
|
|
class="w-full"
|
|
|
|
/>
|
|
|
|
</FormField>
|
|
|
|
|
|
|
|
<TextInputList
|
|
|
|
name="mailalias"
|
|
|
|
type="email"
|
|
|
|
:label="$t('mail_addresses')"
|
|
|
|
:input-label="$t('mail_address')"
|
|
|
|
:button-label="$t('add_mail')"
|
|
|
|
:placeholder="$t('new_mail')"
|
|
|
|
class="mb-10"
|
2023-08-06 16:27:24 +02:00
|
|
|
/>
|
2023-08-28 19:15:59 +02:00
|
|
|
|
2023-11-18 17:08:22 +01:00
|
|
|
<TextInputList
|
|
|
|
name="mailforward"
|
|
|
|
type="email"
|
|
|
|
:label="$t('mail_forwards')"
|
|
|
|
:input-label="$t('mail_forward')"
|
|
|
|
:button-label="$t('add_forward')"
|
|
|
|
:placeholder="$t('new_forward')"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<fieldset class="basis-1/2 mt-10 lg:mt-0">
|
|
|
|
<legend class="text-xl mb-3">{{ $t('change_password') }}</legend>
|
2023-08-28 19:15:59 +02:00
|
|
|
|
2023-11-18 17:08:22 +01:00
|
|
|
<FormField
|
2023-08-29 16:31:38 +02:00
|
|
|
name="currentpassword"
|
2023-11-18 17:08:22 +01:00
|
|
|
:label="$t('current_password')"
|
|
|
|
class="mb-3"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
name="currentpassword"
|
|
|
|
type="password"
|
|
|
|
autocomplete="current-password"
|
|
|
|
class="w-full"
|
|
|
|
/>
|
|
|
|
</FormField>
|
2023-08-28 19:15:59 +02:00
|
|
|
|
2023-11-18 17:08:22 +01:00
|
|
|
<FormField
|
2023-08-29 16:31:38 +02:00
|
|
|
name="newpassword"
|
2023-11-18 17:08:22 +01:00
|
|
|
:label="$t('new_password')"
|
|
|
|
:description="$t('good_practices_about_user_password')"
|
|
|
|
class="mb-3"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
name="newpassword"
|
|
|
|
type="password"
|
|
|
|
autocomplete="new-password"
|
|
|
|
class="w-full"
|
|
|
|
/>
|
|
|
|
</FormField>
|
|
|
|
|
|
|
|
<FormField
|
|
|
|
name="confirmpassword"
|
|
|
|
:label="$t('confirm_new_password')"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
name="confirmpassword"
|
|
|
|
type="password"
|
|
|
|
class="w-full"
|
|
|
|
/>
|
|
|
|
</FormField>
|
|
|
|
</fieldset>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Success + generic error announcement -->
|
|
|
|
<BaseAlert v-show="feedback" v-bind="feedback" class="mb-10" />
|
|
|
|
|
|
|
|
<!-- SR "loading" announcement -->
|
|
|
|
<BaseAlert
|
|
|
|
:message="loading ? $t('api.processing') : ''"
|
|
|
|
class="sr-only"
|
|
|
|
assertive
|
|
|
|
/>
|
|
|
|
|
|
|
|
<div class="flex mt-10">
|
|
|
|
<NuxtLink to="/" class="btn ms-auto me-2">
|
|
|
|
{{ $t('cancel') }}
|
|
|
|
</NuxtLink>
|
|
|
|
<SubmitButton :loading="loading" variant="success" />
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</section>
|
2023-08-06 16:27:24 +02:00
|
|
|
</div>
|
2023-07-26 04:09:10 +02:00
|
|
|
</template>
|