mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2026-04-04 20:13:42 +00:00
Refactor ThirdPartyEdit.vue to improve validation handling and streamline input components.
- Replaced individual validation logic with `useViolationList` composable for centralization and consistency. - Enhanced input components with floating labels, validation error feedback, and improved class binding for better UX. - Updated API to include `WriteThirdPartyViolationMap` interface for structured validation error mapping. - Refactored imports and adjusted spacing for better readability and adherence to coding standards.
This commit is contained in:
@@ -14,6 +14,22 @@ export const getThirdparty = async (id: number) : Promise<Thirdparty> => {
|
||||
});
|
||||
};
|
||||
|
||||
export interface WriteThirdPartyViolationMap
|
||||
extends Record<string, Record<string, string>> {
|
||||
email: {
|
||||
"{{ value }}": string;
|
||||
},
|
||||
name: {
|
||||
"{{ value }}": string;
|
||||
},
|
||||
telephone: {
|
||||
"{{ value }}": string;
|
||||
}
|
||||
telephone2: {
|
||||
"{{ value }}": string;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* POST a new thirdparty
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<div v-if="parent">
|
||||
<div class="parent-info">
|
||||
<i class="fa fa-li fa-hand-o-right" />
|
||||
<i class="fa fa-li fa-hand-o-right"/>
|
||||
<b class="me-2">{{ trans(THIRDPARTY_MESSAGES_CHILD_OF) }}</b>
|
||||
<span class="chill-entity badge-thirdparty">{{ parent.text }}</span>
|
||||
</div>
|
||||
@@ -56,104 +56,146 @@
|
||||
|
||||
<div v-if="thirdParty.kind === 'child' || thirdParty.kind === 'contact'">
|
||||
<div class="child-info">
|
||||
<div class="input-group mb-3 input-section">
|
||||
<select
|
||||
class="form-select form-select-lg"
|
||||
id="civility"
|
||||
v-model="civility"
|
||||
>
|
||||
<option selected disabled :value="null">
|
||||
{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_CIVILITY) }}
|
||||
</option>
|
||||
<option
|
||||
v-for="civility in civilities"
|
||||
:key="civility.id"
|
||||
:value="civility.id"
|
||||
>
|
||||
{{ localizeString(civility.name) }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="input-section">
|
||||
<div class="mb-3">
|
||||
<div class="input-group">
|
||||
<div class="form-floating">
|
||||
<select
|
||||
class="form-select form-select-lg"
|
||||
name="civility"
|
||||
id="civility"
|
||||
v-model="civility"
|
||||
>
|
||||
<option selected disabled :value="null">
|
||||
{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_CIVILITY) }}
|
||||
</option>
|
||||
<option
|
||||
v-for="civility in civilities"
|
||||
:key="civility.id"
|
||||
:value="civility.id"
|
||||
>
|
||||
{{ localizeString(civility.name) }}
|
||||
</option>
|
||||
</select>
|
||||
<label for="civility">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_CIVILITY) }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group mb-3 input-section">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
v-model="thirdParty.profession"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION)"
|
||||
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION)"
|
||||
aria-describedby="profession"
|
||||
/>
|
||||
<div class="input-section">
|
||||
<div class="mb-3">
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
name="profession"
|
||||
v-model="thirdParty.profession"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION)"
|
||||
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION)"
|
||||
aria-describedby="profession"
|
||||
/>
|
||||
<label for="profession">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION) }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="child-info">
|
||||
<div class="input-section">
|
||||
<div class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="firstname"
|
||||
v-model="thirdParty.firstname"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_FIRSTNAME)"
|
||||
/>
|
||||
<label for="firstname">{{
|
||||
trans(THIRDPARTY_MESSAGES_THIRDPARTY_FIRSTNAME)
|
||||
}}</label>
|
||||
</div>
|
||||
<div v-if="queryItems">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li
|
||||
v-for="(qi, i) in queryItems"
|
||||
:key="i"
|
||||
@click="addQueryItem('firstName', qi)"
|
||||
>
|
||||
<span class="person-text">{{ qi }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mb-3">
|
||||
<div class="input-group">
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="firstname"
|
||||
v-model="thirdParty.firstname"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_FIRSTNAME)"
|
||||
/>
|
||||
<label for="firstname">{{
|
||||
trans(THIRDPARTY_MESSAGES_THIRDPARTY_FIRSTNAME)
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="queryItems">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li
|
||||
v-for="(qi, i) in queryItems"
|
||||
:key="i"
|
||||
@click="addQueryItem('firstName', qi)"
|
||||
>
|
||||
<span class="person-text">{{ qi }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-section">
|
||||
<div class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="name"
|
||||
v-model="thirdParty.name"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_LASTNAME)"
|
||||
/>
|
||||
<label for="name">{{
|
||||
trans(THIRDPARTY_MESSAGES_THIRDPARTY_LASTNAME)
|
||||
}}</label>
|
||||
</div>
|
||||
<div v-if="queryItems">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li
|
||||
v-for="(qi, i) in queryItems"
|
||||
:key="i"
|
||||
@click="addQueryItem('name', qi)"
|
||||
>
|
||||
<span class="person-text">{{ qi }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mb3">
|
||||
<div class="input-group has-validation">
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:class="{ 'is-invalid': violations.hasViolation('name') }"
|
||||
id="name"
|
||||
v-model="thirdParty.name"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_LASTNAME)"
|
||||
/>
|
||||
<label for="name">{{
|
||||
trans(THIRDPARTY_MESSAGES_THIRDPARTY_LASTNAME)
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="err in violations.violationTitles('name')"
|
||||
class="invalid-feedback was-validated-force"
|
||||
>
|
||||
{{ err }}
|
||||
</div>
|
||||
<div v-if="queryItems">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li
|
||||
v-for="(qi, i) in queryItems"
|
||||
:key="i"
|
||||
@click="addQueryItem('name', qi)"
|
||||
>
|
||||
<span class="person-text">{{ qi }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="thirdParty.kind === 'company'">
|
||||
<div class="form-floating mb-3">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
id="name"
|
||||
v-model="thirdParty.name"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_NAME)"
|
||||
/>
|
||||
<label for="name">{{
|
||||
trans(THIRDPARTY_MESSAGES_THIRDPARTY_NAME)
|
||||
}}</label>
|
||||
</div>
|
||||
<div v-if="query">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li @click="addQuery(query)">
|
||||
<span class="person-text">{{ query }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="mb-3">
|
||||
<div class="input-group">
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:class="{ 'is-invalid': violations.hasViolation('name') }"
|
||||
id="name"
|
||||
v-model="thirdParty.name"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_NAME)"
|
||||
/>
|
||||
<label for="name">{{
|
||||
trans(THIRDPARTY_MESSAGES_THIRDPARTY_NAME)
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="err in violations.violationTitles('name')"
|
||||
class="invalid-feedback was-validated-force"
|
||||
>
|
||||
{{ err }}
|
||||
</div>
|
||||
|
||||
<div v-if="query">
|
||||
<ul class="list-suggest add-items inline">
|
||||
<li @click="addQuery(query)">
|
||||
<span class="person-text">{{ query }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -167,49 +209,86 @@
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text" id="email"
|
||||
><i class="fa fa-fw fa-envelope"
|
||||
/></span>
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
v-model="thirdParty.email"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_EMAIL)"
|
||||
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_EMAIL)"
|
||||
aria-describedby="email"
|
||||
/>
|
||||
<div class="mb-3">
|
||||
<div class="input-group has-validation">
|
||||
<span id="email" class="input-group-text">
|
||||
<i class="fa fa-fw fa-at"/>
|
||||
</span>
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:class="{ 'is-invalid': violations.hasViolation('email') }"
|
||||
name="email"
|
||||
v-model="thirdParty.email"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_EMAIL)"
|
||||
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_EMAIL)"
|
||||
aria-describedby="email"
|
||||
/>
|
||||
<label for="email">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_EMAIL) }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="err in violations.violationTitles('email')"
|
||||
class="invalid-feedback was-validated-force"
|
||||
>
|
||||
{{ err }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text" id="phonenumber"
|
||||
><i class="fa fa-fw fa-phone"
|
||||
/></span>
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
v-model="thirdParty.telephone"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER)"
|
||||
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER)"
|
||||
aria-describedby="phonenumber"
|
||||
/>
|
||||
<div class="mb-3">
|
||||
<div class="input-group has-validation">
|
||||
<span class="input-group-text" id="phonenumber">
|
||||
<i class="fa fa-fw fa-phone"/>
|
||||
</span>
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:class="{'is-invalid': violations.hasViolation('telephone') }"
|
||||
name="phonenumber"
|
||||
v-model="thirdParty.telephone"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER)"
|
||||
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER)"
|
||||
aria-describedby="phonenumber"
|
||||
/>
|
||||
<label for="phonenumber">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER) }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="err in violations.violationTitles('telephone')"
|
||||
class="invalid-feedback was-validated-force"
|
||||
>
|
||||
{{ err }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text" id="phonenumber2"
|
||||
><i class="fa fa-fw fa-phone"
|
||||
/></span>
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
v-model="thirdParty.telephone2"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER2)"
|
||||
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER2)"
|
||||
aria-describedby="phonenumber2"
|
||||
/>
|
||||
<div class="mb-3">
|
||||
<div class="input-group has-validation">
|
||||
<span class="input-group-text" id="phonenumber2"><i class="fa fa-fw fa-phone"/></span>
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:class="{ 'is-invalid': violations.hasViolation('telephone2') }"
|
||||
name="phonenumber2"
|
||||
v-model="thirdParty.telephone2"
|
||||
:placeholder="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER2)"
|
||||
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER2)"
|
||||
aria-describedby="phonenumber2"
|
||||
/>
|
||||
<label for="phonenumber2">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER2) }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="err in violations.violationTitles('telephone2')"
|
||||
class="invalid-feedback was-validated-force"
|
||||
>
|
||||
{{ err }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="props.action !== 'addContact'">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text" id="comment"
|
||||
><i class="fa fa-fw fa-pencil"
|
||||
><i class="fa fa-fw fa-pencil"
|
||||
/></span>
|
||||
<textarea
|
||||
class="form-control form-control-lg"
|
||||
@@ -222,12 +301,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue'
|
||||
import {ref, reactive, computed, onMounted, getCurrentInstance} from 'vue'
|
||||
import ThirdPartyRenderBox from '../Entity/ThirdPartyRenderBox.vue'
|
||||
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
|
||||
import {createThirdParty, getThirdparty} from '../../_api/OnTheFly'
|
||||
import {createThirdParty, getThirdparty, WriteThirdPartyViolationMap} from '../../_api/OnTheFly'
|
||||
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue'
|
||||
import { localizeString as _localizeString } from 'ChillMainAssets/lib/localizationHelper/localizationHelper'
|
||||
import {localizeString as _localizeString} from 'ChillMainAssets/lib/localizationHelper/localizationHelper'
|
||||
import {
|
||||
trans,
|
||||
THIRDPARTY_MESSAGES_THIRDPARTY_FIRSTNAME,
|
||||
@@ -248,6 +327,8 @@ import {
|
||||
import {Thirdparty, ThirdpartyCompany, ThirdPartyKind, ThirdPartyWrite} from "../../../types";
|
||||
import {Civility, SetCivility} from "ChillMainAssets/types";
|
||||
import {isValidationException} from "ChillMainAssets/lib/api/apiMethods";
|
||||
import {useViolationList} from "ChillMainAssets/vuejs/_composables/violationList";
|
||||
import {useToast} from "vue-toast-notification";
|
||||
|
||||
|
||||
interface ThirdPartyEditWriteProps {
|
||||
@@ -273,7 +354,9 @@ const props = withDefaults(defineProps<ThirdPartyEditProps>(), {type: 'thirdpart
|
||||
const emit =
|
||||
defineEmits<(e: "onThirdPartyCreated", payload: { thirdParty: Thirdparty }) => void>();
|
||||
|
||||
defineExpose({ postThirdParty });
|
||||
defineExpose({postThirdParty});
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const thirdParty = ref<ThirdPartyWrite>({
|
||||
type: "thirdparty",
|
||||
@@ -290,7 +373,7 @@ const thirdParty = ref<ThirdPartyWrite>({
|
||||
parent: null,
|
||||
});
|
||||
|
||||
const civility = computed<number|null>({
|
||||
const civility = computed<number | null>({
|
||||
get: () => {
|
||||
if (thirdParty.value.civility !== null) {
|
||||
return thirdParty.value.civility.id;
|
||||
@@ -300,7 +383,7 @@ const civility = computed<number|null>({
|
||||
},
|
||||
set: (value: number | null) => {
|
||||
thirdParty.value.civility =
|
||||
value !== null ? { id: value, type: "chill_main_civility" } : null;
|
||||
value !== null ? {id: value, type: "chill_main_civility"} : null;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -310,8 +393,8 @@ const addAddress = reactive({
|
||||
options: {
|
||||
openPanesInModal: true,
|
||||
onlyButton: false,
|
||||
button: { size: 'btn-sm' },
|
||||
title: { create: 'add_an_address_title', edit: 'edit_address' },
|
||||
button: {size: 'btn-sm'},
|
||||
title: {create: 'add_an_address_title', edit: 'edit_address'},
|
||||
},
|
||||
})
|
||||
const addAddressRef = ref<any>(null)
|
||||
@@ -330,7 +413,7 @@ const kind = computed<ThirdPartyKind>({
|
||||
|
||||
const context = computed(() => {
|
||||
const ctx: any = {
|
||||
target: { name: props.type, id: props.id },
|
||||
target: {name: props.type, id: props.id},
|
||||
edit: false,
|
||||
addressId: null as number | null,
|
||||
defaults: (window as any).addaddress,
|
||||
@@ -387,7 +470,7 @@ async function loadData(): Promise<void> {
|
||||
|
||||
function submitAddress(payload: { addressId: number }) {
|
||||
console.log(payload);
|
||||
thirdParty.value.address = {id: payload.addressId};
|
||||
thirdParty.value.address = {id: payload.addressId};
|
||||
}
|
||||
|
||||
function addQueryItem(field: 'name' | 'firstName', queryItem: string) {
|
||||
@@ -409,19 +492,30 @@ function addQuery(query: string) {
|
||||
thirdParty.value.name = query
|
||||
}
|
||||
|
||||
const violations = useViolationList<WriteThirdPartyViolationMap>();
|
||||
|
||||
async function postThirdParty(): Promise<void> {
|
||||
try {
|
||||
const tp = await createThirdParty(thirdParty.value);
|
||||
|
||||
emit('onThirdPartyCreated', { thirdParty: tp });
|
||||
emit('onThirdPartyCreated', {thirdParty: tp});
|
||||
} catch (e: unknown) {
|
||||
throw e;
|
||||
if (isValidationException<WriteThirdPartyViolationMap>(e)) {
|
||||
violations.setValidationException(e);
|
||||
} else {
|
||||
toast.error("An error occurred while creating the third party");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.was-validated-force {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.parent-info {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -429,6 +523,7 @@ async function postThirdParty(): Promise<void> {
|
||||
.child-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.input-section {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user