mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
refactor: update form fields definition in views
This commit is contained in:
parent
c17da2c93e
commit
b2a233f1f1
9 changed files with 130 additions and 151 deletions
app/src
types
views
|
@ -259,7 +259,7 @@ export type FormField<
|
||||||
link?:
|
link?:
|
||||||
| { text: string; name: RouteLocationRaw }
|
| { text: string; name: RouteLocationRaw }
|
||||||
| { text: string; href: string }
|
| { text: string; href: string }
|
||||||
rules?: FormFieldRules<MV> | ComputedRef<FormFieldRules<MV>>
|
rules?: FormFieldRules<MV>
|
||||||
prepend?: string
|
prepend?: string
|
||||||
readonly?: false
|
readonly?: false
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,7 @@ export type FormFieldReadonly<
|
||||||
label: string
|
label: string
|
||||||
cols?: Cols
|
cols?: Cols
|
||||||
readonly: true
|
readonly: true
|
||||||
rules: undefined
|
rules?: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FormFieldDisplay<
|
export type FormFieldDisplay<
|
||||||
|
@ -281,7 +281,7 @@ export type FormFieldDisplay<
|
||||||
visible?: boolean | ComputedRef<boolean>
|
visible?: boolean | ComputedRef<boolean>
|
||||||
hr?: boolean
|
hr?: boolean
|
||||||
readonly?: true
|
readonly?: true
|
||||||
rules: undefined
|
rules?: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FormFieldProps<
|
export type FormFieldProps<
|
||||||
|
@ -293,7 +293,7 @@ export type FormFieldProps<
|
||||||
export type FormFieldReadonlyProps<
|
export type FormFieldReadonlyProps<
|
||||||
C extends AnyWritableComponents,
|
C extends AnyWritableComponents,
|
||||||
MV extends any,
|
MV extends any,
|
||||||
> = Omit<FormFieldReadonly<C>, 'hr' | 'visible' | 'readonly'> & {
|
> = Omit<FormFieldReadonly<C>, 'hr' | 'visible' | 'readonly' | 'rules'> & {
|
||||||
modelValue?: MV
|
modelValue?: MV
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter, type LocationQueryValue } from 'vue-router'
|
import { useRouter, type LocationQueryValue } from 'vue-router'
|
||||||
|
|
||||||
|
@ -26,12 +26,12 @@ const form = ref({
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
})
|
})
|
||||||
const fields = reactive({
|
const fields = {
|
||||||
username: {
|
username: {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('user_username'),
|
label: t('user_username'),
|
||||||
rules: { required, alphalownumdot_ },
|
rules: { required, alphalownumdot_ },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'username',
|
id: 'username',
|
||||||
autocomplete: 'username',
|
autocomplete: 'username',
|
||||||
},
|
},
|
||||||
|
@ -41,13 +41,13 @@ const fields = reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('password'),
|
label: t('password'),
|
||||||
rules: { required, passwordLenght: minLength(4) },
|
rules: { required, passwordLenght: minLength(4) },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'password',
|
id: 'password',
|
||||||
type: 'password',
|
type: 'password',
|
||||||
autocomplete: 'current-password',
|
autocomplete: 'current-password',
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['password']>,
|
} satisfies FieldProps<'InputItem', Form['password']>,
|
||||||
} satisfies FormFieldDict<Form>)
|
} satisfies FormFieldDict<Form>
|
||||||
|
|
||||||
const { v, onSubmit } = useForm(form, fields)
|
const { v, onSubmit } = useForm(form, fields)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import api from '@/api'
|
||||||
import { APIBadRequestError } from '@/api/errors'
|
import { APIBadRequestError } from '@/api/errors'
|
||||||
import { useForm } from '@/composables/form'
|
import { useForm } from '@/composables/form'
|
||||||
import { useAutoModal } from '@/composables/useAutoModal'
|
import { useAutoModal } from '@/composables/useAutoModal'
|
||||||
import { asUnreffed } from '@/helpers/commons'
|
|
||||||
import {
|
import {
|
||||||
alphalownumdot_,
|
alphalownumdot_,
|
||||||
minLength,
|
minLength,
|
||||||
|
@ -26,7 +25,7 @@ type Steps = 'start' | 'domain' | 'user' | 'rootfsspace-error' | 'login'
|
||||||
const step = ref<Steps>('start')
|
const step = ref<Steps>('start')
|
||||||
const serverError = ref('')
|
const serverError = ref('')
|
||||||
const domain = ref('')
|
const domain = ref('')
|
||||||
const dyndns_recovery_password = ref('')
|
const dyndns_recovery_password = ref<string | undefined>()
|
||||||
|
|
||||||
type Form = typeof form.value
|
type Form = typeof form.value
|
||||||
const form = ref({
|
const form = ref({
|
||||||
|
@ -35,25 +34,25 @@ const form = ref({
|
||||||
password: '',
|
password: '',
|
||||||
confirmation: '',
|
confirmation: '',
|
||||||
})
|
})
|
||||||
const fields = reactive({
|
const fields = {
|
||||||
// FIXME satisfies FormFieldDict but not for CardForm?
|
// FIXME satisfies FormFieldDict but not for CardForm?
|
||||||
alert: {
|
alert: {
|
||||||
component: 'ReadOnlyAlertItem',
|
component: 'ReadOnlyAlertItem',
|
||||||
props: { label: t('postinstall.user.first_user_help'), type: 'info' },
|
cProps: { label: t('postinstall.user.first_user_help'), type: 'info' },
|
||||||
} satisfies FieldProps<'ReadOnlyAlertItem'>,
|
} satisfies FieldProps<'ReadOnlyAlertItem'>,
|
||||||
|
|
||||||
username: {
|
username: {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('user_username'),
|
label: t('user_username'),
|
||||||
rules: { required, alphalownumdot_ },
|
rules: { required, alphalownumdot_ },
|
||||||
props: { id: 'username', placeholder: t('placeholder.username') },
|
cProps: { id: 'username', placeholder: t('placeholder.username') },
|
||||||
} satisfies FieldProps<'InputItem', Form['username']>,
|
} satisfies FieldProps<'InputItem', Form['username']>,
|
||||||
|
|
||||||
fullname: {
|
fullname: {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('user_fullname'),
|
label: t('user_fullname'),
|
||||||
rules: { required, name },
|
rules: { required, name },
|
||||||
props: { id: 'fullname', placeholder: t('placeholder.fullname') },
|
cProps: { id: 'fullname', placeholder: t('placeholder.fullname') },
|
||||||
} satisfies FieldProps<'InputItem', Form['fullname']>,
|
} satisfies FieldProps<'InputItem', Form['fullname']>,
|
||||||
|
|
||||||
password: {
|
password: {
|
||||||
|
@ -62,21 +61,19 @@ const fields = reactive({
|
||||||
description: t('good_practices_about_admin_password'),
|
description: t('good_practices_about_admin_password'),
|
||||||
descriptionVariant: 'warning',
|
descriptionVariant: 'warning',
|
||||||
rules: { required, passwordLenght: minLength(8) },
|
rules: { required, passwordLenght: minLength(8) },
|
||||||
props: { id: 'password', placeholder: '••••••••', type: 'password' },
|
cProps: { id: 'password', placeholder: '••••••••', type: 'password' },
|
||||||
} satisfies FieldProps<'InputItem', Form['password']>,
|
} satisfies FieldProps<'InputItem', Form['password']>,
|
||||||
|
|
||||||
confirmation: {
|
confirmation: reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('password_confirmation'),
|
label: t('password_confirmation'),
|
||||||
rules: asUnreffed(
|
rules: computed(() => ({
|
||||||
computed(() => ({
|
|
||||||
required,
|
required,
|
||||||
passwordMatch: sameAs(form.value.password),
|
passwordMatch: sameAs(form.value.password),
|
||||||
})),
|
})),
|
||||||
),
|
cProps: { id: 'confirmation', placeholder: '••••••••', type: 'password' },
|
||||||
props: { id: 'confirmation', placeholder: '••••••••', type: 'password' },
|
}) satisfies FieldProps<'InputItem', Form['confirmation']>,
|
||||||
} satisfies FieldProps<'InputItem', Form['confirmation']>,
|
} satisfies FormFieldDict<Form>
|
||||||
} satisfies FormFieldDict<Form>)
|
|
||||||
|
|
||||||
const { v, onSubmit, serverErrors } = useForm(form, fields)
|
const { v, onSubmit, serverErrors } = useForm(form, fields)
|
||||||
|
|
||||||
|
@ -85,7 +82,10 @@ function goToStep(step_: Steps) {
|
||||||
step.value = step_
|
step.value = step_
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDomain(data: { domain: string; dyndns_recovery_password: string }) {
|
function setDomain(data: {
|
||||||
|
domain: string
|
||||||
|
dyndns_recovery_password?: string
|
||||||
|
}) {
|
||||||
domain.value = data.domain
|
domain.value = data.domain
|
||||||
dyndns_recovery_password.value = data.dyndns_recovery_password
|
dyndns_recovery_password.value = data.dyndns_recovery_password
|
||||||
goToStep('user')
|
goToStep('user')
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { useDomains } from '@/composables/data'
|
import { useDomains } from '@/composables/data'
|
||||||
import { useForm } from '@/composables/form'
|
import { useForm } from '@/composables/form'
|
||||||
import { asUnreffed } from '@/helpers/commons'
|
|
||||||
import {
|
import {
|
||||||
domain,
|
domain,
|
||||||
dynDomain,
|
dynDomain,
|
||||||
|
@ -12,7 +11,7 @@ import {
|
||||||
required,
|
required,
|
||||||
sameAs,
|
sameAs,
|
||||||
} from '@/helpers/validators'
|
} from '@/helpers/validators'
|
||||||
import { formatForm } from '@/helpers/yunohostArguments'
|
import { formatFormValue } from '@/helpers/yunohostArguments'
|
||||||
import type { AdressModelValue, FieldProps, FormFieldDict } from '@/types/form'
|
import type { AdressModelValue, FieldProps, FormFieldDict } from '@/types/form'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
|
@ -31,7 +30,7 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
submit: [data: Partial<{ domain: string; dyndns_recovery_password: string }>]
|
submit: [data: { domain: string; dyndns_recovery_password?: string }]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
@ -63,26 +62,24 @@ const form = ref<Form>({
|
||||||
dynDomainPasswordConfirmation: '',
|
dynDomainPasswordConfirmation: '',
|
||||||
localDomain: { localPart: '', separator: '.', domain: 'local' },
|
localDomain: { localPart: '', separator: '.', domain: 'local' },
|
||||||
})
|
})
|
||||||
const fields = reactive({
|
const fields = {
|
||||||
domain: {
|
domain: reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('domain_name'),
|
label: t('domain_name'),
|
||||||
rules: asUnreffed(
|
rules: computed(() => {
|
||||||
computed(() =>
|
return selected.value === 'domain' ? { required, domain } : undefined
|
||||||
selected.value === 'domain' ? { required, domain } : undefined,
|
}),
|
||||||
),
|
cProps: {
|
||||||
),
|
|
||||||
props: {
|
|
||||||
id: 'domain',
|
id: 'domain',
|
||||||
placeholder: t('placeholder.domain'),
|
placeholder: t('placeholder.domain'),
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['domain']>,
|
}) satisfies FieldProps<'InputItem', Form['domain']>,
|
||||||
|
|
||||||
dynDomain: {
|
dynDomain: {
|
||||||
component: 'AdressItem',
|
component: 'AdressItem',
|
||||||
label: t('domain_name'),
|
label: t('domain_name'),
|
||||||
rules: { localPart: { required, dynDomain } },
|
rules: { localPart: { required, dynDomain } },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'dyn-domain',
|
id: 'dyn-domain',
|
||||||
placeholder: t('placeholder.domain').split('.')[0],
|
placeholder: t('placeholder.domain').split('.')[0],
|
||||||
type: 'domain',
|
type: 'domain',
|
||||||
|
@ -90,51 +87,51 @@ const fields = reactive({
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'AdressItem', Form['dynDomain']>,
|
} satisfies FieldProps<'AdressItem', Form['dynDomain']>,
|
||||||
|
|
||||||
dynDomainPassword: {
|
dynDomainPassword: reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('domain.add.dyn_dns_password'),
|
label: t('domain.add.dyn_dns_password'),
|
||||||
description: t('domain.add.dyn_dns_password_desc'),
|
description: t('domain.add.dyn_dns_password_desc'),
|
||||||
rules: { passwordLenght: minLength(8) },
|
rules: computed(() => {
|
||||||
props: {
|
return selected.value === 'dynDomain'
|
||||||
|
? { required, passwordLenght: minLength(8) }
|
||||||
|
: undefined
|
||||||
|
}),
|
||||||
|
cProps: {
|
||||||
id: 'dyn-dns-password',
|
id: 'dyn-dns-password',
|
||||||
placeholder: '••••••••',
|
placeholder: '••••••••',
|
||||||
type: 'password',
|
type: 'password',
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['dynDomainPassword']>,
|
}) satisfies FieldProps<'InputItem', Form['dynDomainPassword']>,
|
||||||
|
|
||||||
dynDomainPasswordConfirmation: {
|
dynDomainPasswordConfirmation: reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('password_confirmation'),
|
label: t('password_confirmation'),
|
||||||
rules: asUnreffed(
|
rules: computed(() => ({
|
||||||
computed(() => ({
|
|
||||||
passwordMatch: sameAs(form.value.dynDomainPassword),
|
passwordMatch: sameAs(form.value.dynDomainPassword),
|
||||||
})),
|
})),
|
||||||
),
|
cProps: {
|
||||||
props: {
|
|
||||||
id: 'dyn-dns-password-confirmation',
|
id: 'dyn-dns-password-confirmation',
|
||||||
placeholder: '••••••••',
|
placeholder: '••••••••',
|
||||||
type: 'password',
|
type: 'password',
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['dynDomainPasswordConfirmation']>,
|
}) satisfies FieldProps<'InputItem', Form['dynDomainPasswordConfirmation']>,
|
||||||
|
|
||||||
localDomain: {
|
localDomain: reactive({
|
||||||
component: 'AdressItem',
|
component: 'AdressItem',
|
||||||
label: t('domain_name'),
|
label: t('domain_name'),
|
||||||
rules: asUnreffed(
|
rules: computed(() =>
|
||||||
computed(() =>
|
|
||||||
selected.value === 'localDomain'
|
selected.value === 'localDomain'
|
||||||
? { localPart: { required, dynDomain } }
|
? { localPart: { required, dynDomain } }
|
||||||
: undefined,
|
: undefined,
|
||||||
),
|
),
|
||||||
),
|
cProps: {
|
||||||
props: {
|
|
||||||
id: 'dyn-domain',
|
id: 'dyn-domain',
|
||||||
placeholder: t('placeholder.domain').split('.')[0],
|
placeholder: t('placeholder.domain').split('.')[0],
|
||||||
type: 'domain',
|
type: 'domain',
|
||||||
choices: ['local', 'test'],
|
choices: ['local', 'test'],
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'AdressItem', Form['localDomain']>,
|
}) satisfies FieldProps<'AdressItem', Form['localDomain']>,
|
||||||
} satisfies FormFieldDict<Form>)
|
} satisfies FormFieldDict<Form>
|
||||||
|
|
||||||
const { v, onSubmit, serverErrors } = useForm(form, fields)
|
const { v, onSubmit, serverErrors } = useForm(form, fields)
|
||||||
|
|
||||||
|
@ -168,16 +165,13 @@ const localDomainIsVisible = computed(() => {
|
||||||
const onDomainAdd = onSubmit(async () => {
|
const onDomainAdd = onSubmit(async () => {
|
||||||
const domainType = selected.value
|
const domainType = selected.value
|
||||||
if (!domainType) return
|
if (!domainType) return
|
||||||
|
const domain = await formatFormValue(form.value[domainType])
|
||||||
|
|
||||||
const data = await formatForm(
|
emit('submit', {
|
||||||
{
|
domain,
|
||||||
domain: form.value[domainType],
|
|
||||||
dyndns_recovery_password:
|
dyndns_recovery_password:
|
||||||
domainType === 'dynDomain' ? form.value.dynDomainPassword : '',
|
domainType === 'dynDomain' ? form.value.dynDomainPassword : undefined,
|
||||||
},
|
})
|
||||||
{ removeEmpty: true },
|
|
||||||
)
|
|
||||||
emit('submit', data)
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -214,7 +208,7 @@ const onDomainAdd = onSubmit(async () => {
|
||||||
<FormField
|
<FormField
|
||||||
v-bind="fields.domain"
|
v-bind="fields.domain"
|
||||||
v-model="form.domain"
|
v-model="form.domain"
|
||||||
:validation="v.domain"
|
:validation="v.form.domain"
|
||||||
/>
|
/>
|
||||||
</BCollapse>
|
</BCollapse>
|
||||||
|
|
||||||
|
@ -242,7 +236,7 @@ const onDomainAdd = onSubmit(async () => {
|
||||||
:key="key"
|
:key="key"
|
||||||
v-bind="fields[key]"
|
v-bind="fields[key]"
|
||||||
v-model="form[key]"
|
v-model="form[key]"
|
||||||
:validation="v[key]"
|
:validation="v.form[key]"
|
||||||
/>
|
/>
|
||||||
</BCollapse>
|
</BCollapse>
|
||||||
|
|
||||||
|
@ -272,7 +266,7 @@ const onDomainAdd = onSubmit(async () => {
|
||||||
<FormField
|
<FormField
|
||||||
v-bind="fields.localDomain"
|
v-bind="fields.localDomain"
|
||||||
v-model="form.localDomain"
|
v-model="form.localDomain"
|
||||||
:validation="v.localDomain"
|
:validation="v.form.localDomain"
|
||||||
/>
|
/>
|
||||||
</BCollapse>
|
</BCollapse>
|
||||||
</CardForm>
|
</CardForm>
|
||||||
|
|
|
@ -18,7 +18,7 @@ const fields = {
|
||||||
label: t('group_name'),
|
label: t('group_name'),
|
||||||
description: t('group_format_name_help'),
|
description: t('group_format_name_help'),
|
||||||
rules: { required, alphalownumdot_ },
|
rules: { required, alphalownumdot_ },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'groupname',
|
id: 'groupname',
|
||||||
placeholder: t('placeholder.groupname'),
|
placeholder: t('placeholder.groupname'),
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,7 +35,7 @@ const fields = {
|
||||||
component: 'SelectItem',
|
component: 'SelectItem',
|
||||||
label: t('action'),
|
label: t('action'),
|
||||||
rules: { required },
|
rules: { required },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'input-action',
|
id: 'input-action',
|
||||||
choices: [
|
choices: [
|
||||||
{ value: 'allow', text: t('open') },
|
{ value: 'allow', text: t('open') },
|
||||||
|
@ -49,7 +49,7 @@ const fields = {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('port'),
|
label: t('port'),
|
||||||
rules: { number: required, integer, between: between(0, 65535) },
|
rules: { number: required, integer, between: between(0, 65535) },
|
||||||
props: { id: 'input-port', placeholder: '0', type: 'number' },
|
cProps: { id: 'input-port', placeholder: '0', type: 'number' },
|
||||||
} satisfies FieldProps<'InputItem', Form['action']>,
|
} satisfies FieldProps<'InputItem', Form['action']>,
|
||||||
|
|
||||||
connection: {
|
connection: {
|
||||||
|
@ -57,7 +57,7 @@ const fields = {
|
||||||
component: 'SelectItem',
|
component: 'SelectItem',
|
||||||
label: t('connection'),
|
label: t('connection'),
|
||||||
rules: { required },
|
rules: { required },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'input-connection',
|
id: 'input-connection',
|
||||||
choices: [
|
choices: [
|
||||||
{ value: 'ipv4', text: t('ipv4') },
|
{ value: 'ipv4', text: t('ipv4') },
|
||||||
|
@ -71,7 +71,7 @@ const fields = {
|
||||||
component: 'SelectItem',
|
component: 'SelectItem',
|
||||||
label: t('protocol'),
|
label: t('protocol'),
|
||||||
rules: { required },
|
rules: { required },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'input-protocol',
|
id: 'input-protocol',
|
||||||
choices: [
|
choices: [
|
||||||
{ value: 'TCP', text: t('tcp') },
|
{ value: 'TCP', text: t('tcp') },
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import api from '@/api'
|
||||||
import { useDomains, useUsersAndGroups } from '@/composables/data'
|
import { useDomains, useUsersAndGroups } from '@/composables/data'
|
||||||
import { useForm } from '@/composables/form'
|
import { useForm } from '@/composables/form'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
import { asUnreffed } from '@/helpers/commons'
|
|
||||||
import {
|
import {
|
||||||
alphalownumdot_,
|
alphalownumdot_,
|
||||||
minLength,
|
minLength,
|
||||||
|
@ -35,39 +34,37 @@ type Form = typeof form.value
|
||||||
const form = ref({
|
const form = ref({
|
||||||
username: '',
|
username: '',
|
||||||
fullname: '',
|
fullname: '',
|
||||||
domain: '',
|
domain: mainDomain.value,
|
||||||
password: '',
|
password: '',
|
||||||
confirmation: '',
|
confirmation: '',
|
||||||
})
|
})
|
||||||
const fields = {
|
const fields = {
|
||||||
username: {
|
username: reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('user_username'),
|
label: t('user_username'),
|
||||||
rules: asUnreffed(
|
rules: computed(() => ({
|
||||||
computed(() => ({
|
|
||||||
required,
|
required,
|
||||||
alphalownumdot_,
|
alphalownumdot_,
|
||||||
notInUsers: unique(usernames),
|
notInUsers: unique(usernames),
|
||||||
})),
|
})),
|
||||||
),
|
cProps: {
|
||||||
props: {
|
|
||||||
id: 'username',
|
id: 'username',
|
||||||
placeholder: t('placeholder.username'),
|
placeholder: t('placeholder.username'),
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['username']>,
|
}) satisfies FieldProps<'InputItem', Form['username']>,
|
||||||
|
|
||||||
fullname: {
|
fullname: {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
hr: true,
|
hr: true,
|
||||||
label: t('user_fullname'),
|
label: t('user_fullname'),
|
||||||
rules: { required, name },
|
rules: { required, name },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'fullname',
|
id: 'fullname',
|
||||||
placeholder: t('placeholder.fullname'),
|
placeholder: t('placeholder.fullname'),
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['fullname']>,
|
} satisfies FieldProps<'InputItem', Form['fullname']>,
|
||||||
|
|
||||||
domain: {
|
domain: reactive({
|
||||||
component: 'SelectItem',
|
component: 'SelectItem',
|
||||||
hr: true,
|
hr: true,
|
||||||
id: 'mail',
|
id: 'mail',
|
||||||
|
@ -75,8 +72,8 @@ const fields = {
|
||||||
description: t('tip_about_user_email'),
|
description: t('tip_about_user_email'),
|
||||||
descriptionVariant: 'info',
|
descriptionVariant: 'info',
|
||||||
rules: { required },
|
rules: { required },
|
||||||
props: { choices: asUnreffed(domainsAsChoices) },
|
cProps: { choices: domainsAsChoices },
|
||||||
} satisfies FieldProps<'SelectItem', Form['domain']>,
|
}) satisfies FieldProps<'SelectItem', Form['domain']>,
|
||||||
|
|
||||||
password: {
|
password: {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
|
@ -84,28 +81,26 @@ const fields = {
|
||||||
description: t('good_practices_about_user_password'),
|
description: t('good_practices_about_user_password'),
|
||||||
descriptionVariant: 'warning',
|
descriptionVariant: 'warning',
|
||||||
rules: { required, passwordLenght: minLength(8) },
|
rules: { required, passwordLenght: minLength(8) },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'password',
|
id: 'password',
|
||||||
placeholder: '••••••••',
|
placeholder: '••••••••',
|
||||||
type: 'password',
|
type: 'password',
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['password']>,
|
} satisfies FieldProps<'InputItem', Form['password']>,
|
||||||
|
|
||||||
confirmation: {
|
confirmation: reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('password_confirmation'),
|
label: t('password_confirmation'),
|
||||||
rules: asUnreffed(
|
rules: computed(() => ({
|
||||||
computed(() => ({
|
|
||||||
required,
|
required,
|
||||||
passwordMatch: sameAs(form.value.password),
|
passwordMatch: sameAs(form.value.password),
|
||||||
})),
|
})),
|
||||||
),
|
cProps: {
|
||||||
props: {
|
|
||||||
id: 'confirmation',
|
id: 'confirmation',
|
||||||
placeholder: '••••••••',
|
placeholder: '••••••••',
|
||||||
type: 'password',
|
type: 'password',
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['confirmation']>,
|
}) satisfies FieldProps<'InputItem', Form['confirmation']>,
|
||||||
} satisfies FormFieldDict<Form>
|
} satisfies FormFieldDict<Form>
|
||||||
|
|
||||||
const { v, onSubmit } = useForm(form, fields)
|
const { v, onSubmit } = useForm(form, fields)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import type ViewBase from '@/components/globals/ViewBase.vue'
|
||||||
import { useDomains, useUsersAndGroups } from '@/composables/data'
|
import { useDomains, useUsersAndGroups } from '@/composables/data'
|
||||||
import { useArrayRule, useForm } from '@/composables/form'
|
import { useArrayRule, useForm } from '@/composables/form'
|
||||||
import { useInitialQueries } from '@/composables/useInitialQueries'
|
import { useInitialQueries } from '@/composables/useInitialQueries'
|
||||||
import { arrayDiff, asUnreffed } from '@/helpers/commons'
|
import { arrayDiff } from '@/helpers/commons'
|
||||||
import {
|
import {
|
||||||
emailForward,
|
emailForward,
|
||||||
emailLocalPart,
|
emailLocalPart,
|
||||||
|
@ -58,30 +58,25 @@ const fields = reactive({
|
||||||
username: {
|
username: {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('user_username'),
|
label: t('user_username'),
|
||||||
props: {
|
cProps: { id: 'username', disabled: true },
|
||||||
id: 'username',
|
|
||||||
disabled: true,
|
|
||||||
},
|
|
||||||
} satisfies FieldProps<'InputItem', Form['username']>,
|
} satisfies FieldProps<'InputItem', Form['username']>,
|
||||||
|
|
||||||
fullname: {
|
fullname: {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('user_fullname'),
|
label: t('user_fullname'),
|
||||||
rules: { required, nameValidator },
|
rules: { required, nameValidator },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'fullname',
|
id: 'fullname',
|
||||||
placeholder: t('placeholder.fullname'),
|
placeholder: t('placeholder.fullname'),
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['fullname']>,
|
} satisfies FieldProps<'InputItem', Form['fullname']>,
|
||||||
|
|
||||||
mail: {
|
mail: reactive({
|
||||||
component: 'AdressItem',
|
component: 'AdressItem',
|
||||||
label: t('user_email'),
|
label: t('user_email'),
|
||||||
rules: {
|
rules: { localPart: { required, email: emailLocalPart } },
|
||||||
localPart: { required, email: emailLocalPart },
|
cProps: { id: 'mail', choices: domainsAsChoices },
|
||||||
},
|
}) satisfies FieldProps<'AdressItem', Form['mail']>,
|
||||||
props: { id: 'mail', choices: asUnreffed(domainsAsChoices) },
|
|
||||||
} satisfies FieldProps<'AdressItem', Form['mail']>,
|
|
||||||
|
|
||||||
mailbox_quota: {
|
mailbox_quota: {
|
||||||
append: 'M',
|
append: 'M',
|
||||||
|
@ -90,35 +85,34 @@ const fields = reactive({
|
||||||
description: t('mailbox_quota_description'),
|
description: t('mailbox_quota_description'),
|
||||||
// example: t('mailbox_quota_example'),
|
// example: t('mailbox_quota_example'),
|
||||||
rules: { integer, minValue: minValue(0) },
|
rules: { integer, minValue: minValue(0) },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'mailbox-quota',
|
id: 'mailbox-quota',
|
||||||
placeholder: t('mailbox_quota_placeholder'),
|
placeholder: t('mailbox_quota_placeholder'),
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['mailbox_quota']>,
|
} satisfies FieldProps<'InputItem', Form['mailbox_quota']>,
|
||||||
|
|
||||||
mail_aliases: {
|
mail_aliases: reactive({
|
||||||
component: 'AdressItem',
|
component: 'AdressItem',
|
||||||
rules: asUnreffed(
|
rules: useArrayRule(() => form.value.mail_aliases, {
|
||||||
useArrayRule(() => form.value.mail_aliases, {
|
|
||||||
localPart: { required, email: emailLocalPart },
|
localPart: { required, email: emailLocalPart },
|
||||||
}),
|
}),
|
||||||
),
|
cProps: {
|
||||||
props: {
|
|
||||||
placeholder: t('placeholder.username'),
|
placeholder: t('placeholder.username'),
|
||||||
choices: asUnreffed(domainsAsChoices),
|
choices: domainsAsChoices,
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'AdressItem', Form['mail_aliases']>,
|
}) satisfies FieldProps<'AdressItem', Form['mail_aliases']>,
|
||||||
|
|
||||||
mail_forward: {
|
mail_forward: reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
rules: asUnreffed(
|
rules: useArrayRule(() => form.value.mail_forward, {
|
||||||
useArrayRule(() => form.value.mail_forward, { required, emailForward }),
|
required,
|
||||||
),
|
emailForward,
|
||||||
props: {
|
}),
|
||||||
|
cProps: {
|
||||||
placeholder: t('user_new_forward'),
|
placeholder: t('user_new_forward'),
|
||||||
type: 'email',
|
type: 'email',
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['mail_forward']>,
|
}) satisfies FieldProps<'InputItem', Form['mail_forward']>,
|
||||||
|
|
||||||
change_password: {
|
change_password: {
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
|
@ -126,7 +120,7 @@ const fields = reactive({
|
||||||
description: t('good_practices_about_user_password'),
|
description: t('good_practices_about_user_password'),
|
||||||
descriptionVariant: 'warning',
|
descriptionVariant: 'warning',
|
||||||
rules: { passwordLenght: minLength(8) },
|
rules: { passwordLenght: minLength(8) },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'change_password',
|
id: 'change_password',
|
||||||
type: 'password',
|
type: 'password',
|
||||||
placeholder: '••••••••',
|
placeholder: '••••••••',
|
||||||
|
@ -134,19 +128,19 @@ const fields = reactive({
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['change_password']>,
|
} satisfies FieldProps<'InputItem', Form['change_password']>,
|
||||||
|
|
||||||
confirmation: {
|
confirmation: reactive({
|
||||||
component: 'InputItem',
|
component: 'InputItem',
|
||||||
label: t('password_confirmation'),
|
label: t('password_confirmation'),
|
||||||
rules: asUnreffed(
|
rules: computed(() => ({
|
||||||
computed(() => ({ passwordMatch: sameAs(form.value.change_password) })),
|
passwordMatch: sameAs(form.value.change_password),
|
||||||
),
|
})),
|
||||||
props: {
|
cProps: {
|
||||||
id: 'confirmation',
|
id: 'confirmation',
|
||||||
type: 'password',
|
type: 'password',
|
||||||
placeholder: '••••••••',
|
placeholder: '••••••••',
|
||||||
autocomplete: 'new-password',
|
autocomplete: 'new-password',
|
||||||
},
|
},
|
||||||
} satisfies FieldProps<'InputItem', Form['confirmation']>,
|
}) satisfies FieldProps<'InputItem', Form['confirmation']>,
|
||||||
} satisfies FormFieldDict<Form>)
|
} satisfies FormFieldDict<Form>)
|
||||||
|
|
||||||
const { v, onSubmit } = useForm(form, fields)
|
const { v, onSubmit } = useForm(form, fields)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
@ -21,13 +21,13 @@ const form = ref({
|
||||||
update: false,
|
update: false,
|
||||||
delete: false,
|
delete: false,
|
||||||
})
|
})
|
||||||
const fields = reactive({
|
const fields = {
|
||||||
csvfile: {
|
csvfile: {
|
||||||
component: 'FileItem',
|
component: 'FileItem',
|
||||||
label: t('users_import_csv_file'),
|
label: t('users_import_csv_file'),
|
||||||
description: t('users_import_csv_file_desc'),
|
description: t('users_import_csv_file_desc'),
|
||||||
rules: { file: required },
|
rules: { file: required },
|
||||||
props: {
|
cProps: {
|
||||||
id: 'csvfile',
|
id: 'csvfile',
|
||||||
accept: 'text/csv',
|
accept: 'text/csv',
|
||||||
placeholder: t('placeholder.file'),
|
placeholder: t('placeholder.file'),
|
||||||
|
@ -38,20 +38,16 @@ const fields = reactive({
|
||||||
label: t('users_import_update'),
|
label: t('users_import_update'),
|
||||||
description: t('users_import_update_desc'),
|
description: t('users_import_update_desc'),
|
||||||
component: 'CheckboxItem',
|
component: 'CheckboxItem',
|
||||||
props: {
|
cProps: { id: 'update' },
|
||||||
id: 'update',
|
|
||||||
},
|
|
||||||
} satisfies FieldProps<'CheckboxItem', Form['update']>,
|
} satisfies FieldProps<'CheckboxItem', Form['update']>,
|
||||||
|
|
||||||
delete: {
|
delete: {
|
||||||
component: 'CheckboxItem',
|
component: 'CheckboxItem',
|
||||||
label: t('users_import_delete'),
|
label: t('users_import_delete'),
|
||||||
description: t('users_import_delete_desc'),
|
description: t('users_import_delete_desc'),
|
||||||
props: {
|
cProps: { id: 'delete' },
|
||||||
id: 'delete',
|
|
||||||
},
|
|
||||||
} satisfies FieldProps<'CheckboxItem', Form['delete']>,
|
} satisfies FieldProps<'CheckboxItem', Form['delete']>,
|
||||||
} satisfies FormFieldDict<Form>)
|
} satisfies FormFieldDict<Form>
|
||||||
|
|
||||||
const { v, onSubmit } = useForm(form, fields)
|
const { v, onSubmit } = useForm(form, fields)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue