410 lines
11 KiB
Vue
410 lines
11 KiB
Vue
<template>
|
|
<div :class="`crud-document-input-${intId} row`">
|
|
<!-- FILE LANGUAGE -->
|
|
<div class="col-12 col-md-6 mb-3">
|
|
<label
|
|
for="selectLanguageDocument"
|
|
class="col-form-label"
|
|
>
|
|
<b>{{ trans.methods.__("Langue du document") }} *</b>
|
|
</label>
|
|
<select
|
|
id="selectLanguageDocument"
|
|
name="lang"
|
|
class="form-select"
|
|
aria-label="Select language for the document"
|
|
@click="languageDocument($event)"
|
|
role="button"
|
|
>
|
|
<option
|
|
v-for="(language, languageIndex) in intLanguages"
|
|
:key="languageIndex"
|
|
:value="language.value"
|
|
:selected="Number(intModel?.lang) === language.value ? true : false"
|
|
>
|
|
{{ language.label }}
|
|
</option>
|
|
</select>
|
|
<small class="form-text text-muted">
|
|
{{ trans.methods.__("Choisissez une langue parmis la liste.") }}
|
|
</small>
|
|
</div>
|
|
<!-- INPUT NAME -->
|
|
<div class="col-12 col-md-6 mb-3">
|
|
<label
|
|
for="documentTitle"
|
|
class="col-form-label"
|
|
>
|
|
<b>{{ trans.methods.__("Titre du document") }} *</b>
|
|
</label>
|
|
<WordCounter
|
|
id="documentTitle"
|
|
class="ck-word-count"
|
|
/>
|
|
<input
|
|
id="documentTitle"
|
|
type="text"
|
|
name="title"
|
|
class="form-control need-word-counter"
|
|
:placeholder="trans.methods.__('Un beau titre de document')"
|
|
v-model="documentTitle"
|
|
>
|
|
<small class="form-text text-muted">
|
|
{{ trans.methods.__("Doit posséder 3 caractères minimum, 255 maximum.") }}
|
|
</small>
|
|
</div>
|
|
<!-- SELECT DIRECTORY -->
|
|
<div
|
|
v-if="intIsDocFR"
|
|
class="col-12 col-md-6 mb-3"
|
|
>
|
|
<label
|
|
for="directoryDirectoryId"
|
|
class="col-form-label"
|
|
>
|
|
<b>{{ trans.methods.__("Dossier à associer") }} *</b>
|
|
</label>
|
|
<select
|
|
id="directoryDirectoryId"
|
|
class="form-select"
|
|
name="directory_id"
|
|
aria-describedby="directoryDirectoryIdHelp"
|
|
role="button"
|
|
>
|
|
<option
|
|
v-for="(directory, directoryIndex) in intDirectories"
|
|
:key="directoryIndex"
|
|
:value="directory.id"
|
|
:selected="intModel?.directory_id === directory.id ? true : false"
|
|
>
|
|
{{ directory.name }}
|
|
</option>
|
|
</select>
|
|
<small class="form-text text-muted">
|
|
{{ trans.methods.__("Choisissez un dossier à associer parmis la liste.") }}
|
|
</small>
|
|
</div>
|
|
<!-- SELECT FRENCH DOCUMENT -->
|
|
<div
|
|
v-else
|
|
class="col-12 col-md-6 mb-3"
|
|
>
|
|
<label
|
|
for="documentParentId"
|
|
class="col-form-label"
|
|
>
|
|
<b>{{ trans.methods.__("Document français à associer") }} *</b>
|
|
</label>
|
|
<select
|
|
v-if="intDocuments.length > 0"
|
|
id="documentParentId"
|
|
class="form-select"
|
|
name="document_id"
|
|
aria-describedby="documentParentIdHelp"
|
|
role="button"
|
|
>
|
|
<option
|
|
v-for="(document, documentIndex) in intDocuments"
|
|
:key="documentIndex"
|
|
:value="document.id"
|
|
:selected="intModel?.document_id === document.id ? true : false"
|
|
>
|
|
{{ document.title }}
|
|
</option>
|
|
</select>
|
|
<p
|
|
v-else
|
|
class="text-muted"
|
|
>
|
|
{{ trans.methods.__("Aucun documents français n'est disponible") }}
|
|
</p>
|
|
<small class="form-text text-muted">
|
|
{{ trans.methods.__("Choisissez un document français à associer parmis la liste.") }}
|
|
</small>
|
|
</div>
|
|
<!-- CHOOSE FILE -->
|
|
<div class="col-12 col-md-6 mb-3">
|
|
<label
|
|
for="documentInputFile"
|
|
class="col-form-label"
|
|
>
|
|
<b>{{ trans.methods.__("Document à ajouter") }} *</b>
|
|
</label>
|
|
<div :class="intIsUploading ? 'd-none' : ''">
|
|
<div class="input-group">
|
|
<button
|
|
class="btn btn-outline-secondary"
|
|
type="button"
|
|
data-bs="tooltip"
|
|
:title="trans.methods.__('Modifier le document source')"
|
|
>
|
|
<FontAwesomeIcon icon="fa-solid fa-folder-open" />
|
|
</button>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
role="button"
|
|
ref="inputFile"
|
|
:value="
|
|
intDocumentDatas.path
|
|
? intDocumentDatas.path
|
|
: 'Sélectionnez un fichier'
|
|
"
|
|
data-bs="tooltip"
|
|
:title="trans.methods.__('Modifier le document source')"
|
|
readonly
|
|
>
|
|
</div>
|
|
<div v-if="intMessage">
|
|
<p
|
|
class="m-0"
|
|
:class="intSuccess ? 'text-success' : 'text-danger'"
|
|
>
|
|
{{ intMessage }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="progress"
|
|
:class="!intIsUploading ? 'd-none' : ''"
|
|
>
|
|
<div
|
|
class="progress-bar progress-bar-striped progress-bar-animated"
|
|
ref="progressBar"
|
|
role="progressbar"
|
|
aria-label="Progress bar"
|
|
aria-valuenow="0"
|
|
aria-valuemin="0"
|
|
aria-valuemax="100"
|
|
>
|
|
{{ intFilePercent }}%
|
|
</div>
|
|
</div>
|
|
<small class="form-text text-muted">
|
|
{{ trans.methods.__("Doit être au format .pdf ou .mp4.") }}
|
|
</small>
|
|
</div>
|
|
<input
|
|
class="d-none"
|
|
name="path"
|
|
:value="intDocumentDatas.path"
|
|
>
|
|
<input
|
|
class="d-none"
|
|
name="uuid"
|
|
:value="intDocumentDatas.uuid"
|
|
>
|
|
<input
|
|
class="d-none"
|
|
name="type"
|
|
:value="intDocumentDatas.type"
|
|
>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|
import { Tooltip } from "bootstrap";
|
|
import Resumable from "resumablejs";
|
|
import { nextTick, onMounted, ref, useAttrs } from "vue";
|
|
import route from "./../../modules/route";
|
|
import trans from "./../../modules/trans";
|
|
import WordCounter from "./WordCounterComponent.vue";
|
|
|
|
defineOptions({
|
|
name: "AddDocumentComponent"
|
|
});
|
|
|
|
// * ATTRIBUTES
|
|
const attrs = useAttrs();
|
|
|
|
// * REFS
|
|
const inputFile = ref<HTMLInputElement|null>(null);
|
|
const progressBar = ref<HTMLDivElement|null>(null);
|
|
|
|
// * DATA
|
|
const intId = ref<string>("");
|
|
const intMessage = ref<string>("");
|
|
const intSuccess = ref<boolean>(false);
|
|
const intIsUploading = ref<boolean>(false);
|
|
const intFilePercent = ref<number|null>(null);
|
|
const intProgressbar = ref<HTMLDivElement|null>(null);
|
|
const intLanguages = ref<Array<{
|
|
value: number;
|
|
label: string;
|
|
}>>([]);
|
|
const intDocuments = ref<Array<{
|
|
id: number;
|
|
title: string;
|
|
}>>([]);
|
|
const intDirectories = ref<LaravelModel[]>([]);
|
|
const intIsDocFR = ref<boolean>(false);
|
|
const intModel = ref<LaravelModel|null>(null);
|
|
const intR = ref<Resumable|null>(null);
|
|
const intDocumentDatas = ref<{
|
|
directory_id: number;
|
|
document_id: number | null;
|
|
uuid: string;
|
|
title: string;
|
|
type: string;
|
|
path: string;
|
|
lang: string;
|
|
published: boolean;
|
|
}>({
|
|
directory_id: 0,
|
|
document_id: null,
|
|
uuid: "",
|
|
title: "",
|
|
type: "",
|
|
path: "",
|
|
lang: "",
|
|
published: false,
|
|
});
|
|
const intCsrf = ref<string>("");
|
|
const intTooltipList = ref<HTMLButtonElement[]>([]);
|
|
let documentTitle = ref<string>("");
|
|
|
|
onMounted(() => {
|
|
const json = String(attrs.json ?? "{}"),
|
|
data = JSON.parse(json);
|
|
|
|
intId.value = data.id;
|
|
intLanguages.value = Object.values(data.languages);
|
|
intDocuments.value = data.documents;
|
|
intDirectories.value = data.directories;
|
|
intModel.value = data.model;
|
|
if (intModel.value) {
|
|
documentTitle.value = String(intModel.value.title ?? "");
|
|
}
|
|
if (intModel.value != null) {
|
|
if (intModel.value.lang === undefined) {
|
|
intIsDocFR.value = true;
|
|
} else {
|
|
intDocumentDatas.value.path = String(intModel.value.path);
|
|
intDocumentDatas.value.uuid = String(intModel.value.uuid);
|
|
intDocumentDatas.value.type = String(intModel.value.type);
|
|
intIsDocFR.value = intModel.value.lang === 1 ? true : false;
|
|
}
|
|
}
|
|
intCsrf.value = data.csrf;
|
|
nextTick(() => {
|
|
initResumable();
|
|
updateBootstrapTooltip();
|
|
});
|
|
});
|
|
|
|
function languageDocument(e: Event) {
|
|
const target = e.target as HTMLSelectElement;
|
|
intIsDocFR.value = Number(target.value) === intLanguages.value[0].value ? true : false;
|
|
}
|
|
|
|
function initResumable() {
|
|
// Route.
|
|
const uploadFileRoute = route.methods.route("bo.documents.upload");
|
|
if (!uploadFileRoute) {
|
|
throw new Error("Undefined route bo.documents.upload");
|
|
}
|
|
intR.value = new Resumable({
|
|
chunkSize: 1024 * 1024, // 1MB
|
|
simultaneousUploads: 1,
|
|
maxFiles: 1,
|
|
testChunks: false,
|
|
target: uploadFileRoute,
|
|
query: {
|
|
_token: intCsrf.value,
|
|
},
|
|
});
|
|
intR.value.on("fileAdded", fileAdded);
|
|
intR.value.on("fileSuccess", fileSuccess);
|
|
intR.value.on("fileError", fileError);
|
|
intR.value.on("fileProgress", () => {
|
|
if (intR.value !== null) {
|
|
intFilePercent.value = Math.floor(intR.value.progress() * 100);
|
|
if (intProgressbar.value !== null)
|
|
intProgressbar.value.style.width = intFilePercent.value + "%";
|
|
}
|
|
});
|
|
intR.value.assignBrowse(inputFile.value as Element, false);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param file The file would like to store.
|
|
*/
|
|
function fileAdded(file: any) {
|
|
if (
|
|
file.file.type !== "application/pdf" &&
|
|
file.file.type !== "video/mp4"
|
|
) {
|
|
return !(intMessage.value = "Extension du fichier invalide.");
|
|
}
|
|
intIsUploading.value = true;
|
|
setTimeout(() => {
|
|
intProgressbar.value = progressBar.value as HTMLDivElement;
|
|
intProgressbar.value.style.width = intFilePercent.value + "%";
|
|
}, 10);
|
|
closeBootstrapTooltip();
|
|
if (intR.value !== null) intR.value.upload();
|
|
}
|
|
|
|
/**
|
|
* Run method if the file is stored.
|
|
* @param file The file stored.
|
|
* @param message Set a new successful message.
|
|
*/
|
|
function fileSuccess(file: any, message: string) {
|
|
let result = JSON.parse(message);
|
|
intDocumentDatas.value.path = file.fileName;
|
|
intDocumentDatas.value.uuid = result.uid;
|
|
if (file.file.type === "application/pdf") {
|
|
intDocumentDatas.value.type = "pdf";
|
|
} else {
|
|
intDocumentDatas.value.type = "mp4";
|
|
}
|
|
intSuccess.value = true;
|
|
intMessage.value = "Fichier chargé.";
|
|
const intInputFileResult = progressBar.value as HTMLInputElement;
|
|
intInputFileResult.value =
|
|
intDocumentDatas.value.uuid + intDocumentDatas.value.type;
|
|
setTimeout(() => {
|
|
intIsUploading.value = false;
|
|
nextTick(() => {
|
|
updateBootstrapTooltip();
|
|
});
|
|
}, 800);
|
|
}
|
|
|
|
/**
|
|
* Set a new error message.
|
|
*/
|
|
function fileError() {
|
|
intMessage.value = "Une erreur est survenue, veuillez recharger la page.";
|
|
}
|
|
|
|
/**
|
|
* Update Bootstrap tooltips.
|
|
*/
|
|
function updateBootstrapTooltip() {
|
|
let newTooltipList = [].slice.call(
|
|
document.querySelectorAll(
|
|
".crud-document-input-" + intId.value + " [data-bs=\"tooltip\"]"
|
|
)
|
|
) as HTMLButtonElement[];
|
|
let tmp = newTooltipList.filter((x) => !intTooltipList.value.includes(x));
|
|
tmp.map((tooltip) => {
|
|
return new Tooltip(tooltip);
|
|
});
|
|
intTooltipList.value = newTooltipList;
|
|
closeBootstrapTooltip();
|
|
}
|
|
|
|
/**
|
|
* Close all Bootstrap tooltips.
|
|
*/
|
|
function closeBootstrapTooltip() {
|
|
intTooltipList.value.forEach((tooltip) => {
|
|
tooltip.blur();
|
|
Tooltip.getInstance(tooltip)?.hide();
|
|
});
|
|
}
|
|
</script>
|