ldc-crm/resources/ts/bo/components/ClaimMessageComponent.vue

348 lines
9.9 KiB
Vue

<template>
<div
ref="claimMessage"
:class="`${!isSystemMessage ? isAuthenticableUser ? 'auth-user-message' : 'other-message' : 'automatic-message'} d-flex flex-column justify-content-center mb-2`"
>
<div class="message-wrapper d-flex align-items-center w-100">
<!-- BTN VALID UPDATING -->
<div v-if="loading">
<div
class="spinner-border spinner-border-sm me-2"
role="status"
>
<span class="visually-hidden">{{ trans.methods.__('Chargement...') }}</span>
</div>
</div>
<div
v-if="!loading && isAuthenticableUser"
class="d-flex flex-column justify-content-center align-items-center me-1"
>
<!-- SET UPDATING MODE -->
<button
v-if="!updating"
class="btn btn-sm btn-outline-primary border-0 d-flex align-items-center justify-content-center"
data-bs-tooltip="tooltip"
type="button"
:title="trans.methods.__('Modifier votre message')"
@click="setUpdatingMode(true)"
:disabled="loading"
>
<FontAwesomeIcon icon="fa-solid fa-pencil" />
</button>
<button
v-if="updating"
class="btn btn-sm btn-success border-0 d-flex align-items-center justify-content-center mb-1"
data-bs-tooltip="tooltip"
type="button"
:title="trans.methods.__('Valider la modification')"
@click="updateMessage"
:disabled="loading"
>
<FontAwesomeIcon icon="fa-solid fa-check" />
</button>
<!-- BTN CANCEL UPDATING -->
<button
v-if="updating"
class="btn btn-sm btn-danger border-0 d-flex align-items-center justify-content-center"
data-bs-tooltip="tooltip"
type="button"
:title="trans.methods.__('Annuler la modification')"
@click="setUpdatingMode(false)"
:disabled="loading"
>
<FontAwesomeIcon icon="fa-solid fa-xmark" />
</button>
</div>
<!-- MESSAGE CONTENT -->
<div :class="`message-content text-${isAuthenticableUser ? 'white' : 'dark'} text-break shadow-sm rounded-2 p-2 ${updating ? 'w-100' : 'w-fit'}`">
<template v-if="!updating">
<p
class="m-0"
:class="{ 'px-4': isSystemMessage }"
v-html="claimMessageModelContent"
/>
</template>
<textarea
v-if="updating && !isSystemMessage"
ref="messageTextarea"
class="form-control"
:class="{'is-invalid': errors.methods.parseValidationInput('content', allErrors)}"
v-model="claimMessageModelContent"
:disabled="loading"
/>
<small
v-if="Object.keys(allErrors).length"
class="text-danger bg-white rounded-2 px-1 m-0"
>
{{ errors.methods.parseValidationInput('content', allErrors) }}
</small>
</div>
</div>
<small class="message-details d-flex flex-column flex-lg-row text-secondary fst-italic">
<span v-if="isSystemMessage">
{{ trans.methods.__('Message système à :date', {
date: getFormatedTime(String(claimMessageModel.created_at)),
}) }}
</span>
<span v-else>
{{ trans.methods.__(':user à :date', {
user: claimMessageModel.user ? `${claimMessageModel.user?.first_name} ${claimMessageModel.user?.last_name}` : trans.methods.__('Utilisateur supprimé'),
date: getFormatedTime(String(claimMessageModel.created_at)),
}) }}
</span>
<span
v-if="showDates || claimMessageModel.created_at !== claimMessageModel.updated_at"
class="ms-1"
>
{{ trans.methods.__('- Mis à jour le :date', {
date: getFormatedDate(String(showDates ? new Date() : claimMessageModel.updated_at)),
}) }}
</span>
</small>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, nextTick, useAttrs } from "vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import type { PropType } from "vue";
import errors from "./../../modules/errors";
import route from "./../../modules/route";
import trans from "./../../modules/trans";
import { Tooltips } from "./../../modules/tooltips";
defineOptions({
name: "ClaimMessageComponent"
});
// * ATTRIBUTES
const attrs = useAttrs();
// * EMITS
const emits = defineEmits<{
(e: "setLoadingMessage", value: boolean): void;
}>();
// * PROPS
const props = defineProps({
authUserId: {
type: Number,
default: Math.pow(10, 16) / Math.random(),
},
claimMessageModel: {
type: Object as PropType<LaravelModel & LaravelModelUserRelation>,
default: () => { return []; },
},
loading: {
type: Boolean,
default: false,
},
updating: {
type: Boolean,
default: false,
},
showDates: {
type: Boolean,
default: false,
},
allErrors: {
type: Object as PropType<LaravelValidationErrors>,
default: () => { return {}; },
},
});
// * REFS
const claimMessage = ref<HTMLDivElement|null>(null);
const messageTextarea = ref<HTMLElement|null>(null);
// * DATA
const authUserId = ref<number>(props.authUserId);
const claimMessageModel = ref<LaravelModel & LaravelModelUserRelation>(props.claimMessageModel);
const claimMessageModelContent = ref<string>(String(props.claimMessageModel.content ?? ""));
const loading = ref<boolean>(props.loading);
const updating = ref<boolean>(props.updating);
const showDates = ref<boolean>(props.showDates);
const tooltips = ref<Tooltips|null>(null);
const allErrors = ref<LaravelValidationErrors>(props.allErrors);
const MessageType = {
auto: 1,
human: 2,
};
// * COMPUTED
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
const isAuthenticableUser = computed<boolean>(() => claimMessageModel.value.user !== null && claimMessageModel.value.user.id === authUserId.value);
const isSystemMessage = computed<boolean>(() => claimMessageModel.value.type === MessageType.auto);
// * LIFECYCLE
onMounted((): void => {
const json = String(attrs.json ?? "{}"),
data = JSON.parse(json);
if (!isUsedWithProps.value) {
authUserId.value = data.authUserId;
claimMessageModel.value = data.claimMessageModel;
claimMessageModelContent.value = String(claimMessageModel.value.content ?? "");
loading.value = data.loading;
updating.value = data.updating;
allErrors.value = data.allErrors;
}
initTooltips();
});
// * METHODS
/**
* Update the message.
* @return void
*/
function updateMessage(): void {
// Check if there is no changes.
if (claimMessageModelContent.value.trim() === claimMessageModel.value.content) {
claimMessageModelContent.value = claimMessageModelContent.value.trim();
updating.value = false;
nextTick(() => {
tooltips.value?.closeBootstrapTooltip();
initTooltips();
});
return;
}
loading.value = true;
allErrors.value = {};
emits("setLoadingMessage", loading.value);
window.axios
.patch(route.methods.getRoute("bo.claim_messages.update", {
CLAIMMESSAGEID: String(claimMessageModel.value.id),
}), {
claim_message_id: claimMessageModel.value.id,
content: claimMessageModelContent.value,
})
.then((response) => {
claimMessageModel.value.content = response.data.content;
claimMessageModelContent.value = response.data.content;
updating.value = false;
loading.value = false;
showDates.value = true;
nextTick(() => {
messageTextarea.value?.focus();
tooltips.value?.closeBootstrapTooltip();
initTooltips();
});
emits("setLoadingMessage", loading.value);
})
.catch((e) => {
loading.value = false;
emits("setLoadingMessage", loading.value);
allErrors.value = e.response?.data?.errors ?? {};
errors.methods.ajaxErrorHandler(e);
});
}
/**
* Set the updating mode.
* @return void
*/
function setUpdatingMode(value: boolean): void {
updating.value = value;
claimMessageModelContent.value = String(claimMessageModel.value.content);
if (!updating.value) {
allErrors.value = {};
}
nextTick(() => {
messageTextarea.value?.focus();
tooltips.value?.closeBootstrapTooltip();
initTooltips();
});
}
/**
* Return a formated date.
* @return string
*/
function getFormatedDate(unformatedDate: string): string {
const date = new Date(unformatedDate);
return new Intl.DateTimeFormat("fr-FR", {
weekday: "long",
day: "numeric",
month: "long",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
}).format(date);
}
/**
* Return a formated date.
* @return string
*/
function getFormatedTime(unformatedTime: string): string {
const date = new Date(unformatedTime);
return new Intl.DateTimeFormat("fr-FR", {
hour: "2-digit",
minute: "2-digit",
}).format(date);
}
/** Initialise all tooltips in the component. */
function initTooltips(): void {
setTimeout(() => {
tooltips.value = Tooltips.make({
type: "dom",
elements: claimMessage.value!.querySelectorAll("[data-bs-tooltip=\"tooltip\"]")
});
}, 500);
}
</script>
<style lang="scss" scoped>
.message-content {
max-width: 70%;
p {
white-space: pre-line;
}
}
.other-message {
align-items: flex-start;
.message-wrapper, {
justify-content: flex-start;
}
.message-details {
align-items: flex-start;
}
.message-content {
background-color: #e6e6e6;
border-bottom-left-radius: 0 !important;
}
}
.auth-user-message {
align-items: flex-end;
.message-wrapper {
justify-content: flex-end;
}
.message-details {
align-items: flex-end;
}
.message-content {
background-color: #3f3f3f;
border-bottom-right-radius: 0 !important;
}
}
.automatic-message {
align-items: center;
.message-wrapper {
justify-content: center;
}
.message-details {
align-items: center;
}
.message-content {
background-color: #ffcb00;
width: 70%;
text-align: center;
}
}
.btn-sm {
width: 30px !important;
}
</style>