ldc-crm/resources/ts/bo/components/ClaimActionsComponent.vue
2025-10-07 11:32:02 +02:00

501 lines
16 KiB
Vue

<template>
<table
ref="claimActions"
class="table table-hover m-0"
>
<tbody>
<!-- Brand -->
<tr class="border-bottom">
<td
class="align-middle pb-3"
colspan="2"
v-if="canParameters.updateBrand"
>
<label
for="brand"
class="col-form-label"
>
<b>{{ trans.methods.__('Modifier la :model', { model: brandLabel }) }} *</b>
</label>
<SearchBelongsToDropdown
id="brand"
name="brand_id"
:label="trans.methods.__('Modifier la :model', { model: brandLabel })"
:model="claimModel"
targetclass="App\Models\Brand"
:modellistpaginate="brandModels"
:modelselected="(selectParameters.brand as VselectOption)"
:constructedroute="route.methods.getRoute('bo.brands.json-paginate')"
fieldname="name"
:multiple="false"
:required="true"
:disabled="loading || isTreatedClaim"
:placeholder="trans.methods.__('Rechercher une :model', { model: brandLabel })"
@selected-value-changed="setSelectedBrand"
/>
<small class="text-danger m-0">
{{ errors.methods.parseValidationInput('brand_id', allErrors) }}
</small>
</td>
<template v-else>
<td class="align-middle text-capitalize fw-bold">
{{ brandLabel }}
</td>
<td
v-if="claimModel.brand"
class="align-middle"
>
{{ selectParameters.brand.name }}
</td>
</template>
</tr>
<!-- Subject -->
<tr class="border-bottom">
<td
class="align-middle pb-3"
colspan="2"
v-if="canParameters.updateSubject"
>
<label
for="brand"
class="col-form-label"
>
<b>{{ trans.methods.__('Modifier le :model', { model: claimsubjectLabel }) }} *</b>
</label>
<SearchBelongsToDropdown
id="subject"
name="subject_id"
:label="trans.methods.__('Modifier le :model', { model: claimsubjectLabel })"
:model="claimModel"
targetclass="App\Models\ClaimSubject"
:modellistpaginate="claimSubjectModels"
:modelselected="(selectParameters.subject as VselectOption)"
:constructedroute="getClaimSubjectPaginateRoute()"
fieldname="label"
:nodeclass="allErrors.subject_id ? 'form-control is-invalid' : ''"
:multiple="false"
:required="true"
:disabled="loading || isTreatedClaim"
:placeholder="trans.methods.__('Rechercher une :model', { model: claimsubjectLabel })"
@selected-value-changed="setSelectedSubject"
/>
<small class="text-danger m-0">
{{ errors.methods.parseValidationInput('subject_id', allErrors) }}
</small>
</td>
<template v-else>
<td class="align-middle text-capitalize fw-bold">
{{ claimsubjectLabel }}
</td>
<td class="align-middle">
{{ selectParameters.subject?.label }}
</td>
</template>
</tr>
<!-- Enums (Type, Status, ResponseType) -->
<tr
v-for="(enumField, enumFieldIndex) in enumFields"
:key="enumFieldIndex"
:class="{'border-0': enumFieldIndex === Object.keys(enumFields).length - 1}"
>
<td
class="align-middle pb-3"
:class="{'border-0': enumFieldIndex === Object.keys(enumFields).length - 1}"
colspan="2"
v-if="enumField.can"
>
<label
:for="enumField.name"
class="col-form-label"
>
<b>{{ trans.methods.__('Modifier le :label', { label: trans.methods.__(`validation.custom.${enumField.name}`) }) }} *</b>
</label>
<div
:key="localVersion"
class="d-flex justify-content-center align-items-center"
>
<select
:id="enumField.name"
class="form-select"
:class="{'is-invalid': errors.methods.parseValidationInput(`${enumField.name}`, allErrors)}"
:name="enumField.name"
:aria-describedby="`${enumField.name}Help`"
required="true"
role="button"
:disabled="loading || (enumField.name !== 'status') ? isTreatedClaim : false"
@change="setSelectedEnum($event, enumField.name)"
>
<option
v-for="(model, modelIndex) in enumField.associatedEnum"
:key="modelIndex"
:value="model.value"
:selected="claimModel[enumField.name] === model.value"
>
{{ model.label }}
</option>
</select>
<button
v-if="checkIsManualResponseType(enumField.name) && !isTreatedClaim"
type="button"
class="btn btn-primary ms-1"
data-bs-toggle="modal"
data-bs-target="#manualResponseModal"
data-bs-tooltip="tooltip"
:title="trans.methods.__('Répondre au client')"
>
<FontAwesomeIcon icon="fa-solid fa-comment-dots" />
</button>
</div>
<small class="text-danger m-0">
{{ errors.methods.parseValidationInput(`${enumField.name}`, allErrors) }}
</small>
</td>
<template v-else>
<td
class="align-middle fw-bold"
:class="{'border-0': enumFieldIndex === Object.keys(enumFields).length - 1}"
>
{{ ucfirst(trans.methods.__(`validation.custom.${enumField.name}`)) }}
</td>
<td
class="align-middle"
:class="{'border-0': enumFieldIndex === Object.keys(enumFields).length - 1}"
>
{{ Object.values(enumField.associatedEnum).find(item => item.value == selectParameters.enums[enumField.name])?.label }}
</td>
</template>
</tr>
</tbody>
</table>
<ClaimManualResponse
v-if="!isTreatedClaim"
:claim-model="claimModel"
:is-manual-response-type="isManualResponseType"
:claim-template-models="claimTemplateModels"
@answer-sent="updateMessages"
/>
</template>
<script setup lang="ts">
import { computed, useAttrs, onMounted, ref, reactive } from "vue";
import type { PropType } from "vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import errors from "./../../modules/errors";
import trans from "./../../modules/trans";
import route from "./../../modules/route";
import { Tooltips } from "./../../modules/tooltips";
import SearchBelongsToDropdown from "./SearchBelongsToDropdownComponent.vue";
import ClaimManualResponse from "./ClaimManualResponseComponent.vue";
defineOptions({
name: "ClaimActionsComponent",
});
// * ATTRIBUTES
const attrs = useAttrs();
// * EMITS
const emits = defineEmits<{
(e: "updateMessages", value: Array<LaravelModel & LaravelModelUserRelation>): void;
}>();
// * PROPS
const props = defineProps({
authUserId: {
type: Number,
default: Math.pow(10, 16) / Math.random(),
},
claimModel: {
type: Object as PropType<LaravelModel & LaravelModelClaimRelations>,
default: () => { return []; },
},
brandModels: {
type: Object as PropType<LaravelPaginator>,
default: () => { return {}; },
},
claimSubjectModels: {
type: Object as PropType<LaravelPaginator>,
default: () => { return {}; },
},
claimTemplateModels: {
type: Object as PropType<LaravelPaginator>,
default: () => { return {}; },
},
enums: {
type: Object as PropType<Record<string, Array<Enum>>>,
default: () => { return {}; },
},
can: {
type: Object as PropType<Record<string, boolean>>,
default: () => { return {}; },
},
loading: {
type: Boolean,
default: false,
},
isManualResponseType: {
type: Boolean,
default: false,
},
allErrors: {
type: Object as PropType<LaravelValidationErrors>,
default: () => { return {}; },
},
});
// * REFS
const claimActions = ref<HTMLDivElement|null>(null);
// * DATA
const authUserId = ref<number>(props.authUserId);
const claimModel = ref<LaravelModel & LaravelModelClaimRelations>(props.claimModel);
const brandModels = ref<LaravelPaginator>(props.brandModels);
const claimSubjectModels = ref<LaravelPaginator>(props.claimSubjectModels);
const claimTemplateModels = ref<LaravelPaginator>(props.claimTemplateModels);
const loading = ref<boolean>(props.loading);
const isManualResponseType = ref<boolean>(props.isManualResponseType);
const tooltips = ref<Tooltips|null>(null);
const enumFields = ref<Record<number, {
can: boolean,
name: string,
associatedEnum: Array<Enum>,
}>>({});
const allErrors = ref<LaravelValidationErrors>(props.allErrors);
const localVersion = ref<number>(1);
/** Enum parameters. */
const enumParameters = reactive<{
claimType: Array<Enum>,
claimStatus: Array<Enum>,
claimResponseType: Array<Enum>,
}>({
claimType: props.enums.claimType,
claimStatus: props.enums.claimStatus,
claimResponseType: props.enums.claimResponseType,
});
/** Can parameters. */
const canParameters = reactive<{
updateBrand: boolean,
updateSubject: boolean,
updateType: boolean,
updateStatus: boolean,
updateResponseType: boolean,
}>({
updateBrand: props.can.updateBrand,
updateSubject: props.can.updateSubject,
updateType: props.can.updateType,
updateStatus: props.can.updateStatus,
updateResponseType: props.can.updateResponseType,
});
/** Select parameters. */
const selectParameters = reactive<{
brand: LaravelModel,
subject: LaravelModel|null,
enums: Record<string,string>,
}>({
brand: props.claimModel.brand,
subject: props.claimModel.claim_subject,
enums: {
type: String(props.claimModel.type),
status: String(props.claimModel.status),
response_type: String(props.claimModel.response_type),
},
});
// * COMPUTED
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
const brandLabel = computed(() => trans.methods.__("models.classes.brands").toString().toLowerCase().replace(/s$/, ""));
const claimsubjectLabel = computed(() => trans.methods.__("models.classes.claimsubjects").toLowerCase().toString().replace(/s$/, ""));
const isTreatedClaim = computed(() => Object.values(enumParameters.claimStatus).find(e => e.name === "treated")?.value == selectParameters.enums["status"]);
// * LIFECYCLE
onMounted((): void => {
const json = String(attrs.json ?? "{}"),
data = JSON.parse(json);
if (!isUsedWithProps.value) {
authUserId.value = data.authUserId;
claimModel.value = data.claimModel;
claimSubjectModels.value = data.claimSubjectModels;
claimTemplateModels.value = data.claimTemplateModels;
brandModels.value = data.brandModels;
allErrors.value = data.allErrors;
/** Enum parameters. */
enumParameters.claimType = data.enums.claimType;
enumParameters.claimStatus = data.enums.claimStatus;
enumParameters.claimResponseType = data.enums.claimResponseType;
/** Can parameters. */
canParameters.updateBrand = data.can.updateBrand;
canParameters.updateSubject = data.can.updateSubject;
canParameters.updateType = data.can.updateType;
canParameters.updateStatus = data.can.updateStatus;
canParameters.updateResponseType = data.can.updateResponseType;
/** Select parameters. */
selectParameters.brand = claimModel.value.brand;
selectParameters.subject = claimModel.value.claim_subject;
selectParameters.enums.type = String(claimModel.value.type);
selectParameters.enums.status = String(claimModel.value.status);
selectParameters.enums.response_type = String(claimModel.value.response_type);
}
/** Set enum fields. */
enumFields.value = [
{
can: canParameters.updateType,
name: "type",
associatedEnum: enumParameters.claimType,
},
{
can: canParameters.updateStatus,
name: "status",
associatedEnum: enumParameters.claimStatus,
},
{
can: canParameters.updateResponseType,
name: "response_type",
associatedEnum: enumParameters.claimResponseType,
},
];
initTooltips();
});
/**
* Set the selected brand.
* @return void
*/
function setSelectedBrand(brandModel: VselectOption|VselectOption[]|null): void {
selectParameters.brand = brandModel as LaravelModel;
selectParameters.subject = null;
fetchClaimSubjects();
}
/**
* Set the selected subject.
* @return void
*/
function setSelectedSubject(subjectModel: VselectOption|VselectOption[]|null): void {
selectParameters.subject = subjectModel as LaravelModel;
delete allErrors.value.subject_id;
updateClaimData();
}
/**
* Set the selected subject.
* @return void
*/
function setSelectedEnum(event: Event, field: string): void {
const selectEnum = event.target as HTMLSelectElement|null;
selectParameters.enums[field] = selectEnum?.value ?? "";
delete allErrors.value[field];
if (checkIsManualResponseType(field)) {
isManualResponseType.value = true;
} else {
updateClaimData();
}
}
/**
* Fetch all claim subjects from specific brand.
* @return void
*/
function fetchClaimSubjects(): void {
loading.value = true;
window.axios
.get(getClaimSubjectPaginateRoute())
.then((reponse) => {
claimSubjectModels.value = reponse.data;
})
.catch((e) => {
loading.value = false;
allErrors.value = e.response?.data?.errors ?? {};
errors.methods.ajaxErrorHandler(e);
});
}
/**
* Fetch all claim subjects from specific brand.
* @return void
*/
function updateClaimData(): void {
loading.value = true;
window.axios
.patch(route.methods.getRoute("bo.claims.update", {
CLAIMID: String(claimModel.value.id),
}), {
brand_id: selectParameters.brand.id,
subject_id: selectParameters.subject?.id,
type: selectParameters.enums["type"],
status: selectParameters.enums["status"],
response_type: selectParameters.enums["response_type"],
})
.then((reponse) => {
loading.value = false;
emits("updateMessages", reponse.data);
})
.catch((e) => {
loading.value = false;
allErrors.value = e.response?.data?.errors ?? {};
errors.methods.ajaxErrorHandler(e);
});
}
/**
* Return the claim subject route.
* @return string
*/
function getClaimSubjectPaginateRoute(): string {
const targetRoute = "bo.claim_subjects.json-paginate";
const fetchMessageRoute = route.methods.route(targetRoute, {
BRANDID: String(selectParameters.brand.id),
});
if (!fetchMessageRoute) {
throw new Error(`Undefined ${targetRoute}`);
}
return fetchMessageRoute + "?targetModel=App\\Models\\ClaimSubject&page=1&paginate=5&fieldName=label";
}
/**
* Check if it's manual response when the response_type select changed.
* @param boolean fieldName
* @return string
*/
function checkIsManualResponseType(fieldName: string): boolean {
return selectParameters.enums["response_type"] === "2" && fieldName === "response_type";
}
/**
* Event propagation to update all messages in the conversation.
* @param Array value
* @return void
*/
function updateMessages(value: Array<LaravelModel & LaravelModelUserRelation>): void {
selectParameters.enums["status"] = "4";
localVersion.value = localVersion.value + 1;
emits("updateMessages", value);
}
/**
* Initialise all tooltips in the component.
* @return void
*/
function initTooltips(): void {
setTimeout(() => {
tooltips.value = Tooltips.make({
type: "dom",
elements: claimActions.value!.querySelectorAll("[data-bs-tooltip=\"tooltip\"]")
});
}, 500);
}
/**
*Capitalize the first letter.
* @return string
*/
function ucfirst(str: string): string {
if (!str) return "";
return str.charAt(0).toUpperCase() + str.slice(1);
}
</script>