Added Eslint + TS corrections

This commit is contained in:
Jérémy Munsch 2022-08-25 13:31:42 +02:00
parent de29cda24f
commit 847a543dab
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
22 changed files with 2701 additions and 372 deletions

40
.eslintrc.json Normal file
View file

@ -0,0 +1,40 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-strongly-recommended",
"@vue/eslint-config-typescript"
],
"overrides": [
{
"files": ["*.ts", "*.vue"],
"rules": {
"no-undef": "off"
}
},
{
"files": ["*.vue"],
"rules": {
"indent": "off"
}
}
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["vue", "@typescript-eslint"],
"ignorePatterns": ["webpack.mix.js", "**/vendor/*.js", "**/vendor/**/*.js"],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
"vue/script-indent": ["error", 2, { "baseIndent": 0 }]
}
}

1788
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -15,30 +15,38 @@
"@fortawesome/free-brands-svg-icons": "^6.1.2",
"@fortawesome/free-regular-svg-icons": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.1.2",
"@fortawesome/vue-fontawesome": "^3.0.1",
"@popperjs/core": "^2.11.5",
"@types/bootstrap": "^5.1.12",
"@types/toastify-js": "^1.11.1",
"@types/vue-select": "^3.16.0",
"@types/webpack-env": "^1.17.0",
"@typescript-eslint/eslint-plugin": "^5.35.1",
"@typescript-eslint/parser": "^5.35.1",
"axios": "^0.21.1",
"bootstrap": "^5.1.3",
"clean-webpack-plugin": "^4.0.0",
"cross-env": "^7.0",
"css-loader": "^6.7.1",
"del-cli": "^4.0.1",
"eslint": "^8.22.0",
"eslint-plugin-vue": "^9.4.0",
"eslint-webpack-plugin": "^3.2.0",
"glightbox": "^3.2.0",
"laravel-mix": "^6.0.6",
"laravel-mix-clean": "^0.1.0",
"laravel-mix-eslint": "^0.2.0",
"laravel-mix-polyfill": "^3.0.1",
"lodash": "^4.17.19",
"moment": "^2.29.3",
"prettier": "^2.7.1",
"resolve-url-loader": "^5.0.0",
"sass": "^1.52.1",
"sass-loader": "^12.6.0",
"sweetalert2": "^11.4.16",
"toastify-js": "^1.11.2",
"ts-loader": "^9.3.0",
"typescript": "^4.7.3",
"typescript": "^4.7.4",
"vue": "^3.2.36",
"vue-loader": "^16.8.3",
"vue-select": "^4.0.0-beta.3"

View file

@ -0,0 +1,5 @@
declare module "glightbox" {
export default class GLightbox {
constructor(options: { [key: string]: unknown });
}
}

View file

@ -1,8 +1,12 @@
import axios from "axios";
// @ts-ignore Pas de déclaration ts disponible
import GLightbox from "glightbox";
import * as bootstrap from "bootstrap";
const bootstrap = require("bootstrap");
const metaCSRFToken = document.querySelector("meta[name='csrf-token']");
if (!metaCSRFToken) {
throw new Error("Missing meta CSRF token");
}
/**
* We'll load the axios HTTP library which allows us to easily issue requests
@ -12,17 +16,15 @@ const bootstrap = require("bootstrap");
axios.defaults.headers.common = {
Accept: "application/json, text/plain, */*",
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')!
.getAttribute("content"),
"X-CSRF-TOKEN": metaCSRFToken.getAttribute("content"),
};
window.axios = axios;
window.bootstrap = bootstrap;
// enable bs tooltips
var tooltipTriggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipTriggerList = [].slice.call(
document.querySelectorAll("[data-bs-toggle='tooltip']")
);
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);

View file

@ -0,0 +1,273 @@
<template>
<div v-if="!companies.length">
<span class="h3 text-danger">{{
__("Pour utiliser ce composant il faut des :compagnies", {
compagnies: __("models.classes.companies"),
})
}}</span>
</div>
<div v-else-if="!packages.length">
<span class="h3 text-danger">{{
__("Pour utiliser ce composant il faut des :packages", {
packages: __("models.classes.packages"),
})
}}</span>
</div>
<div
v-else
class="tasks table-responsive"
>
<table class="table">
<thead>
<th>{{ __("Tâches") }}</th>
<th>{{ __("Type") }}</th>
<th width="100">
{{ __("Jours") }}
</th>
<th width="100">
{{ __("Montant") }}
</th>
<th />
</thead>
<tbody v-if="tasks.length">
<tr
v-for="(task, index) in tasks"
:key="index"
>
<td>
<textarea
v-model="task.desc"
rows="2"
class="form-control"
/>
</td>
<td>
<select
v-model="task.type"
@change="updateAmount(index)"
class="form-select"
>
<option
v-for="(typeEnum, index) in taskTypeEnums"
:value="typeEnum.value"
:key="index"
>
{{ typeEnum.label }}
</option>
</select>
<select
v-if="task.type === getTaskTypeEnum('forfait').value"
v-model="task.package"
@change="updateAmount(index)"
class="form-select"
>
<option
v-for="(packageModel, index) in packages"
:value="packageModel"
:key="index"
:selected="task.package === undefined && index === 0"
>
{{ packageModel.name }}
</option>
</select>
</td>
<td>
<input
class="inputNumber"
v-model="task.days"
@change="updateAmount(index)"
type="number"
step="0.125"
min="0"
>
</td>
<td>
<input
v-if="task.type === getTaskTypeEnum('libre').value"
class="inputNumber"
v-model="task.amount"
type="number"
min="0"
>
<template v-else>
{{ task.amount.toFixed(2) }}
</template>&nbsp;€
</td>
<td>
<div
class="btn-group"
role="group"
:aria-label="__('Ajouter ou enlever une ligne')"
>
<button
class="btn btn-sm btn-primary"
@click.prevent="addTasksRow(index)"
>
<font-awesome-icon icon="fa-solid fa-plus" />
</button>
<button
class="btn btn-sm btn-danger"
@click.prevent="removeTasksRow(index)"
>
<font-awesome-icon icon="fa-solid fa-minus" />
</button>
</div>
</td>
</tr>
<tr>
<td
colspan="4"
class="text-end"
>
<button
class="btn btn-primary"
@click.prevent="addTasksRow()"
>
<font-awesome-icon icon="fa-solid fa-plus" />
</button>
</td>
</tr>
<tr>
<td colspan="2">
{{ __("Total") }}
</td>
<td>{{ totalDays }}</td>
<td>{{ Number.parseFloat(String(total)).toFixed(2) }}&nbsp;€</td>
</tr>
</tbody>
</table>
</div>
</template>
<script lang="ts">
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
// import VueSelect from "vue-select";
import { defineComponent } from "vue";
import trans from "../modules/trans";
type TaskRow = {
desc: string;
days: number;
type: number;
amount: number;
package: LaravelModel | null;
};
type TaskType = Enum & { price: number };
export default defineComponent({
mixins: [trans],
components: {
// VueSelect,
FontAwesomeIcon,
},
mounted() {
const json = String(this.$attrs.json ?? "{}"),
data = JSON.parse(json);
this.companies = data.companies;
this.packages = data.packages;
this.taskTypeEnums = data.taskTypeEnums;
this.taskRowModel = data.taskRowModel;
// * Use first enum value as default
this.taskRowModel.type = this.getTaskTypeEnum("web").value as number;
this.taskRowModel.package = this._packages[0];
},
data(): {
tasks: TaskRow[];
companies: LaravelModelList;
packages: LaravelModelList;
taskTypeEnums: TaskType[];
taskRowModel: TaskRow;
} {
return {
tasks: [],
companies: [],
packages: [],
taskTypeEnums: [],
taskRowModel: {
desc: "",
days: 0,
type: 1,
amount: 0,
package: null,
},
};
},
computed__tasks: {
total(): number {
let total = 0;
this.tasks.map((task) => (total += task.amount));
return total;
},
totalDays(): number {
let total = 0;
this.tasks.map((task) => (total += task.days));
return total;
},
},
watch: {},
methods: {
/**
* Get the coresponding task type using value or label
*
* @param type
*/
getTaskTypeEnum(type: string | number): TaskType {
for (let task of this.taskTypeEnums) {
if (
typeof type === "string" &&
task.label.toLowerCase() === type.toLowerCase()
) {
return task;
}
if (typeof type === "number" && task.value === type) {
return task;
}
}
throw new Error(`Task type '${type}' not found`);
},
addTasksRow(beforeIndex: number | null = null): void {
if (beforeIndex !== null) {
this.tasks.splice(beforeIndex, 0, Object.assign({}, this.taskRowModel));
} else this.tasks.push(Object.assign({}, this.taskRowModel));
},
removeTasksRow(beforeIndex: number): void {
this.tasks.splice(beforeIndex, 1);
},
updateAmount(index: number): void {
const task = this.tasks[index];
// task.type === getTaskTypeEnum('web').value ||
// task.type === getTaskTypeEnum('sio').value
switch (task.type) {
case this.getTaskTypeEnum("web").value:
case this.getTaskTypeEnum("sio").value:
task.amount = task.days * this.getTaskTypeEnum(task.type).price;
break;
case this.getTaskTypeEnum("forfait").value:
if (!task.package) {
throw new Error(`task ${task.desc} has undefined package`);
}
task.amount = task.days * (task.package.amount as number);
break;
case this.getTaskTypeEnum("libre").value:
// Do nothing, free input
return;
default:
throw new Error(`Unhandled task type ${task.type}`);
}
this.tasks[index] = task;
},
},
});
</script>
<style lang="scss">
@import "vue-select/src/scss/vue-select.scss";
.tasks {
.inputNumber {
width: 6rem !important;
}
}
</style>

View file

@ -7,17 +7,15 @@
label="label"
:options="labels"
>
<template v-slot:option="{ option }: { option: SelectModel }">
<template #option="{ option }: { option: SelectModel }">
<span
class="py-1 px-2"
:style="`background-color: ${(option as SelectModel).color};color: ${invertColor((option as SelectModel).color, true)};`"
:style="`background-color: ${(option as SelectModel).color};color: ${invertColor((option as SelectModel).color, true)};`"
>
{{ String(option.label) }}
</span>
</template>
<template
v-slot:no-options="{ search, searching }: { search: string, searching: boolean }"
>
<template #no-options>
{{ __("Pus de label disponibles") }}
</template>
</VueSelect>
@ -27,7 +25,7 @@
:key="label.value"
:value="label.value"
:name="`${name}[]`"
/>
>
</div>
</template>
@ -44,7 +42,7 @@ export default defineComponent({
},
mounted() {
const json = String(this.$attrs.json ?? "{}"),
data = JSON.parse(json);
data = JSON.parse(json);
this.name = data.name;
for (let label of data.value ?? []) {
this.selectedLabels.push({
@ -54,7 +52,11 @@ export default defineComponent({
});
}
for (let label of data.labels) {
this.labels.push({ label: label.name, value: label.id, color: label.color });
this.labels.push({
label: label.name,
value: label.id,
color: label.color,
});
}
},
data(): {

View file

@ -1,14 +1,27 @@
<template>
<!-- TODO: order by name, status -->
<div ref="macroschedule" class="macroschedule">
<div
ref="macroschedule"
class="macroschedule"
>
<div class="row mb-3">
<div class="col-1">
<button class="btn btn-primary" v-on:click.prevent="goPreviousWeek()">←</button>
<button
class="btn btn-primary"
@click.prevent="goPreviousWeek()"
>
←
</button>
</div>
<div class="col">
<h1 class="text-secondary">Plan de charge</h1>
<h1 class="text-secondary">
Plan de charge
</h1>
<div class="input-group">
<label for="scheduleDateSelect" class="input-group-text">
<label
for="scheduleDateSelect"
class="input-group-text"
>
Voir date&nbsp;:&nbsp;
</label>
<input
@ -16,8 +29,12 @@
class="form-control"
type="date"
:value="startMoment.format('YYYY-MM-DD')"
v-on:input="showMacroScheduleDate($event.target ? ($event.target as HTMLInputElement).value : '')"
/>
@input="
showMacroScheduleDate(
$event.target ? ($event.target as HTMLInputElement).value : ''
)
"
>
</div>
</div>
<div class="col">
@ -27,7 +44,9 @@
<span
v-for="(activityEnum, index) of activitiesEnum"
:key="index"
:class="`p-1 m-2 border badge badge-activity-${String(activityEnum.value)}`"
:class="`p-1 m-2 border badge badge-activity-${String(
activityEnum.value
)}`"
>
{{ String(activityEnum.label) }}
</span>
@ -41,7 +60,12 @@
</fieldset>
</div>
<div class="col-1">
<button class="btn btn-primary" v-on:click.prevent="goNextWeek()">→</button>
<button
class="btn btn-primary"
@click.prevent="goNextWeek()"
>
→
</button>
</div>
</div>
<div class="row justify-content-center">
@ -49,10 +73,18 @@
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" rowspan="3" class="align-middle">
<th
scope="col"
rowspan="3"
class="align-middle"
>
{{ isAdmin ? "Projets" : "" }}
</th>
<th scope="col" rowspan="3" class="align-middle">
<th
scope="col"
rowspan="3"
class="align-middle"
>
{{ isAdmin ? "Statut" : "" }}
</th>
<th
@ -70,9 +102,8 @@
{{ ucfirst(daysNextMonth[0].format("MMMM")) }}
</th>
</tr>
<tr>
<tr v-if="macroscheduleConfig !== null">
<th
v-if="macroscheduleConfig !== null"
v-for="weekNumber in macroscheduleConfig.numberOfWeeksToShow"
:key="weekNumber"
:colspan="7 - 2"
@ -89,7 +120,7 @@
:key="index"
:class="
'dayCell p-0 text-center ' +
(currentDay.isSame(todayMoment, 'day') ? 'today' : '')
(currentDay.isSame(todayMoment, 'day') ? 'today' : '')
"
>
{{ currentDay.format("dd") }}&nbsp;{{
@ -101,28 +132,36 @@
</thead>
<tbody>
<template v-if="isAdmin">
<tr v-for="(project, index) of projects" :key="index" class="projectRow">
<tr
v-for="(project, index) of projects"
:key="index"
class="projectRow"
>
<th scope="col">
<a
:href="getProjectRoute(project)"
:class="
'rounded-1 p-1 project text-nowrap text-truncate overflow-hidden bg-enum-' +
project.status
project.status
"
>
{{ str_limit(String(project.name), 45) }}
</a>
</th>
<td>
<span class="fw-bold text-nowrap text-truncate overflow-hidden">
{{ String(projectStatusEnum[project.status as number].label) }}
<span
class="fw-bold text-nowrap text-truncate overflow-hidden"
>
{{
String(projectStatusEnum[project.status as number].label)
}}
</span>
</td>
<template v-for="(currentDay, index) of daysToShow">
<td
:class="
'p-0 border border-dark activities ' +
(currentDay.isSame(todayMoment, 'day') ? 'today' : '')
(currentDay.isSame(todayMoment, 'day') ? 'today' : '')
"
v-if="dayShouldbeenSeen(currentDay)"
:key="index"
@ -135,23 +174,30 @@
:key="index"
:class="
getActivityClass(activity) +
' activity ' +
(hoverActivityId === activity.id ? 'hover' : '')
' activity ' +
(hoverActivityId === activity.id ? 'hover' : '')
"
v-on:click="showModale(project, activity, currentDay, index === 0)"
v-on:mouseover="setActivityHover(activity)"
v-on:mouseleave="setActivityHover(activity, false)"
@click="
showModale(project, activity, currentDay, index === 0)
"
@mouseover="setActivityHover(activity)"
@mouseleave="setActivityHover(activity, false)"
>
<div :title="getActivityTooltip(activity)" data-bs-toggle="tooltip">
<div
:title="getActivityTooltip(activity)"
data-bs-toggle="tooltip"
>
&nbsp;
</div>
<template v-if="projectHasMarkersForTheDay(currentDay, project)">
<template
v-if="projectHasMarkersForTheDay(currentDay, project)"
>
<div
class="marker"
data-bs-toggle="tooltip"
data-bs-html="true"
:title="getMarkersTooltip(currentDay, project)"
></div>
/>
</template>
</div>
</td>
@ -159,15 +205,18 @@
</tr>
</template>
<tr class="projectRow">
<th scope="col" class="border-end-0 d-flex justify-content-between">
<th
scope="col"
class="border-end-0 d-flex justify-content-between"
>
<span class="fw-bold h5">Disponibilité</span>
</th>
<td class="border-start-0"></td>
<td class="border-start-0" />
<template v-for="(currentDay, index) of daysToShow">
<td
:class="
'p-0 border border-dark activities ' +
(currentDay.isSame(todayMoment, 'day') ? 'today' : '')
(currentDay.isSame(todayMoment, 'day') ? 'today' : '')
"
v-if="dayShouldbeenSeen(currentDay)"
:key="index"
@ -177,18 +226,22 @@
class="activity text-center"
:style="{
'background-color':
'rgba(0,0,0,' + getLoadPercent(getMorningMoment(currentDay)),
'rgba(0,0,0,' +
getLoadPercent(getMorningMoment(currentDay)),
}"
>
{{
getLoad(getMorningMoment(currentDay)) > 1 ? "&#128293;" : "&nbsp;"
getLoad(getMorningMoment(currentDay)) > 1
? "&#128293;"
: "&nbsp;"
}}
</div>
<div
class="activity text-center"
:style="{
'background-color':
'rgba(0,0,0,' + getLoadPercent(getAfterNoonMoment(currentDay)),
'rgba(0,0,0,' +
getLoadPercent(getAfterNoonMoment(currentDay)),
}"
>
{{
@ -203,10 +256,10 @@
v-if="macroscheduleConfig !== null"
:class="
'activity text-center ' +
(getLoad(getMorningMoment(currentDay)) >
macroscheduleConfig.loadDevThreshold
? 'bg-dark'
: 'bg-light')
(getLoad(getMorningMoment(currentDay)) >
macroscheduleConfig.loadDevThreshold
? 'bg-dark'
: 'bg-light')
"
>
&nbsp;
@ -215,10 +268,10 @@
v-if="macroscheduleConfig !== null"
:class="
'activity text-center ' +
(getLoad(getAfterNoonMoment(currentDay)) >
macroscheduleConfig.loadDevThreshold
? 'bg-dark'
: 'bg-light')
(getLoad(getAfterNoonMoment(currentDay)) >
macroscheduleConfig.loadDevThreshold
? 'bg-dark'
: 'bg-light')
"
>
&nbsp;
@ -248,19 +301,30 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalLabel" ref="modalLabel">
<h5
class="modal-title"
id="modalLabel"
ref="modalLabel"
>
Activité pour {{ modaleProps.name }}
</h5>
</div>
<div class="modal-body">
<div class="container">
<div class="row justify-content-center">
<div v-if="error" class="alert alert-danger" role="alert">
<div
v-if="error"
class="alert alert-danger"
role="alert"
>
{{ error }}
</div>
<div class="col-8">
<div class="form-group mb-3">
<label for="categorie" class="col-form-label text-md-right ms-5">
<label
for="categorie"
class="col-form-label text-md-right ms-5"
>
Activité
</label>
<select
@ -272,7 +336,10 @@
v-model="modaleProps.activity"
required
>
<option :value="undefined" disabled>
<option
:value="undefined"
disabled
>
Sélectionnez une activité
</option>
<option
@ -286,34 +353,41 @@
</select>
</div>
<div class="form-group mb-3">
<label for="start" class="col-form-label text-md-right ms-5"
>Date début</label
>
<label
for="start"
class="col-form-label text-md-right ms-5"
>Date début</label>
<div class="input-group mb-3">
<input
class="form-control"
id="start"
type="date"
name="start"
:value="momentJs(modaleProps.start).format('YYYY-MM-DD')"
v-on:input="updateDateField($event, 'start')"
:value="
momentJs(modaleProps.start).format('YYYY-MM-DD')
"
@input="updateDateField($event, 'start')"
:disabled="modalLoading"
/>
>
<select
class="form-select"
:disabled="modalLoading"
v-model="modaleProps.startMorning"
v-on:input="updateHourField($event, 'start')"
@input="updateHourField($event, 'start')"
>
<option
value="1"
:selected="momentJs(modaleProps.start).format('HH') === '00'"
:selected="
momentJs(modaleProps.start).format('HH') === '00'
"
>
Matin
</option>
<option
value="0"
:selected="momentJs(modaleProps.start).format('HH') === '12'"
:selected="
momentJs(modaleProps.start).format('HH') === '12'
"
>
Midi
</option>
@ -321,9 +395,10 @@
</div>
</div>
<div class="form-group mb-3">
<label for="end" class="col-form-label text-md-right ms-5"
>Date fin</label
>
<label
for="end"
class="col-form-label text-md-right ms-5"
>Date fin</label>
<div class="input-group mb-3">
<input
class="form-control"
@ -331,24 +406,28 @@
type="date"
name="end"
:value="momentJs(modaleProps.end).format('YYYY-MM-DD')"
v-on:input="updateDateField($event, 'end')"
@input="updateDateField($event, 'end')"
:disabled="modalLoading"
/>
>
<select
class="form-select"
:disabled="modalLoading"
v-model="modaleProps.endMorning"
v-on:input="updateHourField($event, 'end')"
@input="updateHourField($event, 'end')"
>
<option
value="1"
:selected="momentJs(modaleProps.end).format('HH') === '11'"
:selected="
momentJs(modaleProps.end).format('HH') === '11'
"
>
Midi
</option>
<option
value="0"
:selected="momentJs(modaleProps.end).format('HH') === '23'"
:selected="
momentJs(modaleProps.end).format('HH') === '23'
"
>
Soir
</option>
@ -356,7 +435,10 @@
</div>
</div>
<div class="form-group mb-3">
<label for="comment" class="col-form-label text-md-right ms-5">
<label
for="comment"
class="col-form-label text-md-right ms-5"
>
Commentaire
</label>
<textarea
@ -366,16 +448,17 @@
id="comment"
rows="2"
placeholder=""
v-on:keydown="limitLines($event)"
@keydown="limitLines($event)"
v-model="modaleProps.comment"
:disabled="modalLoading"
></textarea>
/>
</div>
<div class="form-group mb-3">
<label for="load" class="col-form-label text-md-right ms-5"
>Charge
<i>(nombre de personnes consommées sur la période)</i></label
>
<label
for="load"
class="col-form-label text-md-right ms-5"
>Charge
<i>(nombre de personnes consommées sur la période)</i></label>
<input
type="number"
class="form-control"
@ -384,7 +467,7 @@
v-model="modaleProps.load"
:disabled="modalLoading"
min="1"
/>
>
</div>
</div>
</div>
@ -394,19 +477,19 @@
class="btn btn-primary me-3"
type="button"
:disabled="modalLoading"
v-on:click="saveActivity()"
@click="saveActivity()"
>
<span
v-if="modalLoading"
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
></span>
/>
{{ modalLoading ? "Chargement..." : "Valider" }}
</button>
<button
class="btn btn-danger"
v-on:click.prevent="
@click.prevent="
modale ? modale.hide() : null;
$nextTick(refreshTooltips);
"
@ -441,7 +524,7 @@ export default defineComponent({
window.Toastify = Toastify;
const json = String(this.$attrs.json ?? "{}"),
data = JSON.parse(json);
data = JSON.parse(json);
this.projects = data.projects;
this.loadCollection = data.loadCollection;
@ -452,7 +535,9 @@ export default defineComponent({
this.startDay = data.startDay;
this.isAdmin = data.isAdmin;
this.$nextTick(this.refreshTooltips);
this.modale = new window.bootstrap.Modal(this.$refs.editModal as HTMLElement);
this.modale = new window.bootstrap.Modal(
this.$refs.editModal as HTMLElement
);
(this.$refs.editModal as HTMLElement).addEventListener(
"hide.bs.modal",
function (e: Event) {
@ -547,7 +632,7 @@ export default defineComponent({
},
daysToShow(): Moment[] {
let days = [],
startMoment = this.selectedMoment.clone();
startMoment = this.selectedMoment.clone();
if (!this.macroscheduleConfig) {
return [];
}
@ -561,8 +646,8 @@ export default defineComponent({
},
daysThisMonth(): Moment[] {
let days = this.daysToShow,
currentMonth = days[0]?.month(),
out = [];
currentMonth = days[0]?.month(),
out = [];
for (let day of days) {
if (currentMonth === day.month() && this.dayShouldbeenSeen(day)) {
out.push(day);
@ -572,8 +657,8 @@ export default defineComponent({
},
daysNextMonth(): Moment[] {
let days = this.daysToShow,
currentMonth = days[0]?.month(),
out = [];
currentMonth = days[0]?.month(),
out = [];
for (let day of days) {
if (currentMonth !== day.month() && this.dayShouldbeenSeen(day)) {
out.push(day);
@ -620,12 +705,14 @@ export default defineComponent({
},
updateDateField(e: Event, fieldName: string): void {
const date = this.momentJs(this.modaleProps[fieldName]),
target = e.target,
time =
Number.parseInt(String(this.modaleProps[`${fieldName}Morning`])) === 1
? this.getMorningMoment(date, fieldName === "start")
: this.getAfterNoonMoment(date, fieldName === "start"),
input = target ? this.momentJs((target as HTMLInputElement).value) : null;
target = e.target,
time =
Number.parseInt(String(this.modaleProps[`${fieldName}Morning`])) === 1
? this.getMorningMoment(date, fieldName === "start")
: this.getAfterNoonMoment(date, fieldName === "start"),
input = target
? this.momentJs((target as HTMLInputElement).value)
: null;
if (!input) {
throw new Error("updateDateField target input is undefined");
@ -639,23 +726,23 @@ export default defineComponent({
},
updateHourField(e: Event, fieldName: string): void {
const date = this.momentJs(this.modaleProps[fieldName]),
target = e.target;
target = e.target;
if (target && Number.parseInt((target as HTMLInputElement).value) === 1) {
this.modaleProps[fieldName] = this.getMorningMoment(
date,
fieldName === "start"
).format("YYYY-MM-DD\THH:mm:ss");
).format("YYYY-MM-DDTHH:mm:ss");
} else {
this.modaleProps[fieldName] = this.getAfterNoonMoment(
date,
fieldName === "start"
).format("YYYY-MM-DD\THH:mm:ss");
).format("YYYY-MM-DDTHH:mm:ss");
}
},
saveActivity(): void {
const self = this,
activity = Object.assign({}, this.currentActivity) as LaravelModel,
routeUri = self.route("macroplanning.activity");
activity = Object.assign({}, this.currentActivity) as LaravelModel,
routeUri = self.route("macroplanning.activity");
if (self.error?.length) {
return;
@ -727,7 +814,7 @@ export default defineComponent({
project: LaravelModel,
activity: LaravelModel,
selectedMoment: Moment,
isMorning: boolean = true
isMorning = true
) {
selectedMoment = selectedMoment.clone();
@ -736,22 +823,24 @@ export default defineComponent({
}
const self = this,
startMoment = activity.start
? this.momentJs(String(activity.start))
: isMorning
? this.getMorningMoment(selectedMoment, true)
: this.getAfterNoonMoment(selectedMoment, true),
endMoment = activity.end
? this.momentJs(String(activity.end))
: isMorning
? this.getMorningMoment(selectedMoment, false)
: this.getAfterNoonMoment(selectedMoment, false);
startMoment = activity.start
? this.momentJs(String(activity.start))
: isMorning
? this.getMorningMoment(selectedMoment, true)
: this.getAfterNoonMoment(selectedMoment, true),
endMoment = activity.end
? this.momentJs(String(activity.end))
: isMorning
? this.getMorningMoment(selectedMoment, false)
: this.getAfterNoonMoment(selectedMoment, false);
this.modaleProps = {
activity: activity.activity as number | null,
start: String(activity.start ?? startMoment.format("YYYY-MM-DD\THH:mm:ss")),
start: String(
activity.start ?? startMoment.format("YYYY-MM-DDTHH:mm:ss")
),
startMorning: startMoment.format("HH") === "00" ? 1 : 0,
end: String(activity.end ?? endMoment.format("YYYY-MM-DD\THH:mm:ss")),
end: String(activity.end ?? endMoment.format("YYYY-MM-DDTHH:mm:ss")),
endMorning: endMoment.format("HH") === "11" ? 1 : 0,
load: Number.parseInt(String(activity.load)),
comment: String(activity.comment ?? ""),
@ -781,7 +870,8 @@ export default defineComponent({
if (!activate) {
this.hoverActivityId = null;
} else {
this.hoverActivityId = activity.id !== undefined ? String(activity.id) : "";
this.hoverActivityId =
activity.id !== undefined ? String(activity.id) : "";
}
},
@ -817,7 +907,9 @@ export default defineComponent({
return this.projects[project_id];
},
getActivity(moment: Moment, project: LaravelModel): LaravelModel {
for (let activity of Object.values(project.activities) as LaravelModelList) {
for (let activity of Object.values(
project.activities
) as LaravelModelList) {
if (
moment.isBetween(
this.momentJs(
@ -837,13 +929,13 @@ export default defineComponent({
}
return {};
},
getMorningMoment(moment: Moment, start: boolean = true): Moment {
getMorningMoment(moment: Moment, start = true): Moment {
moment = moment.clone();
return start
? moment.hours(0).minutes(0).seconds(0)
: moment.hours(11).minutes(59).seconds(0);
},
getAfterNoonMoment(moment: Moment, start: boolean = true): Moment {
getAfterNoonMoment(moment: Moment, start = true): Moment {
moment = moment.clone();
return start
? moment.hours(12).minutes(0).seconds(0)
@ -853,15 +945,17 @@ export default defineComponent({
let tooltip = "";
for (let marker of project.markers as LaravelModelList) {
if (moment.isSame(String(marker.marker), "day")) {
tooltip += `${this.momentJs(String(marker.marker)).format("DD/MM/YYYY")} ${
marker.label
}<br>`;
tooltip += `${this.momentJs(String(marker.marker)).format(
"DD/MM/YYYY"
)} ${marker.label}<br>`;
}
}
return tooltip;
},
getProjectRoute(project: LaravelModel): string {
const route = this.route("projects.show", { PROJECT: project.id as number });
const route = this.route("projects.show", {
PROJECT: project.id as number,
});
if (!route) {
throw new Error("undefined route projects.show");
}
@ -869,7 +963,7 @@ export default defineComponent({
},
// * FILTERS
str_limit(value: string, size: number = 25) {
str_limit(value: string, size = 25) {
if (!value) return "";
value = value.toString();
if (value.length <= size) {
@ -892,7 +986,10 @@ export default defineComponent({
moment = this.momentJs(moment.week(), "w");
this.showMacroScheduleDate("", moment.add(1, "week"));
},
showMacroScheduleDate(dateString: string, moment: Moment | null = null): void {
showMacroScheduleDate(
dateString: string,
moment: Moment | null = null
): void {
const url = new URL(String(window.location));
moment = moment ?? this.momentJs(dateString);
(window as Window).location =
@ -914,7 +1011,9 @@ export default defineComponent({
}).showToast();
},
refreshTooltips(): void {
let exisitingToolips = document.querySelectorAll('.tooltip[role="tooltip"]');
let exisitingToolips = document.querySelectorAll(
".tooltip[role=\"tooltip\"]"
);
for (let tooltip of exisitingToolips) {
tooltip.remove();
}
@ -923,7 +1022,7 @@ export default defineComponent({
limitLines(e: KeyboardEvent) {
const comment = this.$refs.comment as HTMLTextAreaElement;
let keynum,
lines = comment.value.split("\n").length;
lines = comment.value.split("\n").length;
if (e.key !== undefined) {
keynum = e.key;
} else if (e.keyCode !== undefined) {
@ -953,7 +1052,9 @@ export default defineComponent({
initTooltips(): void {
// * INIT Bootstrap tooltips
let tooltipTriggerList = [].slice.call(
document.querySelectorAll('.color-picker-component [data-bs-toggle="tooltip"]')
document.querySelectorAll(
".color-picker-component [data-bs-toggle=\"tooltip\"]"
)
);
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new window.bootstrap.Tooltip(tooltipTriggerEl, {

View file

@ -1,7 +1,15 @@
<template>
<div>
<VueSelect v-model="value" :options="labels" @search="addOption" />
<input type="hidden" :value="inputValue" :name="name" />
<VueSelect
v-model="value"
:options="labels"
@search="addOption"
/>
<input
type="hidden"
:value="inputValue"
:name="name"
>
</div>
</template>
@ -15,7 +23,7 @@ export default defineComponent({
},
mounted() {
const json = String(this.$attrs.json ?? "{}"),
data = JSON.parse(json);
data = JSON.parse(json);
this.name = data.name;
this.labels = data.labels;

View file

@ -4,16 +4,17 @@
<div class="col-1 text-center actions_col">
<a
class="sticky-top btn btn-primary mt-5"
v-on:click.prevent="goToPlanning(false)"
>←</a
>
@click.prevent="goToPlanning(false)"
>←</a>
</div>
<div class="col-10">
<table class="table table-bordered align-middle text-center schedules_table">
<table
class="table table-bordered align-middle text-center schedules_table"
>
<thead>
<tr>
<th rowspan="2"></th>
<th rowspan="2" />
<th
:colspan="showWeekends ? 7 : 5"
v-for="item in weeksCells"
@ -24,15 +25,26 @@
</th>
</tr>
<tr>
<th v-for="day in days" :class="`day${day.eday}`" :key="day.num">
<th
v-for="day in days"
:class="`day${day.eday}`"
:key="day.num"
>
{{ day.day }} {{ day.num }}
</th>
</tr>
</thead>
<tbody>
<template v-for="(user, index) in schedules">
<tr v-for="(slot, key) in user" :key="`${String(index)} ${String(key)}`">
<td :rowspan="daySlots" class="resource_ident" v-if="key == 1">
<tr
v-for="(slot, key) in user"
:key="`${String(index)} ${String(key)}`"
>
<td
:rowspan="daySlots"
class="resource_ident"
v-if="key == 1"
>
{{ String(users[index].name) }}
</td>
<td
@ -43,9 +55,9 @@
<div
:class="
'cellsizer ' +
(user[key][day.key as any].type.value
? String(user[key][day.key as any].type.value)
: 'empty')
(user[key][day.key as any].type.value
? String(user[key][day.key as any].type.value)
: 'empty')
"
>
<b v-if="user[key][day.key as any]['model_lie']['value']">
@ -54,9 +66,10 @@
class="text-light bg-dark"
:href="String(getCellObjectInfo(user[key][day.key as any])?.url)"
>
{{ String(user[key][day.key as any]["id_lie"]["label"]) }}
</a> </b
><br />
{{
String(user[key][day.key as any]["id_lie"]["label"])
}}
</a> </b><br>
{{ String(user[key][day.key as any]["text"]) }}
<button
@ -64,8 +77,13 @@
type="button"
data-bs-toggle="modal"
data-bs-target="#editModal"
v-on:click.prevent="
setModalData(index, key, day.key, user[key][day.key as any]['id'])
@click.prevent="
setModalData(
index,
key,
day.key,
user[key][day.key as any]['id']
)
"
>
✎
@ -73,8 +91,13 @@
<button
:class="'btn btn-paste ' + (!clipBoard.set ? 'hide' : '')"
type="button"
v-on:click.prevent="
pasteCell(index, key, day.key, user[key][day.key as any]['id'])
@click.prevent="
pasteCell(
index,
key,
day.key,
user[key][day.key as any]['id']
)
"
>
📋
@ -82,8 +105,13 @@
<button
class="btn btn-copy"
type="button"
v-on:click.prevent="
copyCell(index, key, day.key, user[key][day.key as any]['id'])
@click.prevent="
copyCell(
index,
key,
day.key,
user[key][day.key as any]['id']
)
"
>
<span
@ -96,7 +124,9 @@
"
>
📄
<span style="position: absolute; top: 0.25em; left: 0.25em">
<span
style="position: absolute; top: 0.25em; left: 0.25em"
>
📄
</span>
</span>
@ -105,8 +135,13 @@
:class="
'btn btn-clear ' + (!user[key][day.key as any]['id'] ? 'hide' : '')
"
v-on:click.prevent="
eraseCell(index, key, day.key, user[key][day.key as any]['id'])
@click.prevent="
eraseCell(
index,
key,
day.key,
user[key][day.key as any]['id']
)
"
>
⌫
@ -119,7 +154,10 @@
</table>
</div>
<div class="col-1 text-center actions_col">
<a class="sticky-top btn btn-primary mt-5" @click="goToPlanning(true)">→</a>
<a
class="sticky-top btn btn-primary mt-5"
@click="goToPlanning(true)"
>→</a>
</div>
</div>
@ -134,7 +172,11 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalLabel" ref="modalLabel">
<h5
class="modal-title"
id="modalLabel"
ref="modalLabel"
>
{{ modale.title }}
</h5>
<button
@ -143,17 +185,24 @@
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
/>
</div>
<div class="modal-body">
<div class="container">
<div class="row justify-content-center">
<div v-if="error" class="alert alert-danger" role="alert">
<div
v-if="error"
class="alert alert-danger"
role="alert"
>
{{ error }}
</div>
<div class="col-8">
<div class="form-group mb-3">
<label for="categorie" class="col-form-label text-md-right ms-5">
<label
for="categorie"
class="col-form-label text-md-right ms-5"
>
Categorie
</label>
<select
@ -162,7 +211,9 @@
class="form-select"
aria-label="Sélectionnez une catégorie"
>
<option value="">Sélectionnez une catégorie</option>
<option value="">
Sélectionnez une catégorie
</option>
<option
v-for="(activity, index) of activities"
:key="index"
@ -175,7 +226,10 @@
</select>
</div>
<div class="form-group mb-3">
<label for="morph" class="col-form-label text-md-right ms-5">
<label
for="morph"
class="col-form-label text-md-right ms-5"
>
Type d'élément lié
</label>
<select
@ -185,7 +239,9 @@
@change="selectChange"
aria-label="Sélectionnez un élément à lier"
>
<option value="">Sélectionnez un type d'objet à lier</option>
<option value="">
Sélectionnez un type d'objet à lier
</option>
<option
v-for="(model, index) of models"
:key="index"
@ -196,8 +252,14 @@
</option>
</select>
</div>
<div class="form-group mb-3" v-if="modale.dDisplay">
<label for="select_id_lie" class="col-form-label text-md-right ms-5">
<div
class="form-group mb-3"
v-if="modale.dDisplay"
>
<label
for="select_id_lie"
class="col-form-label text-md-right ms-5"
>
Élément lié
</label>
<VueSelect
@ -214,9 +276,12 @@
:searchable="true"
@input="setSelected"
>
<li slot="list-footer" class="pagination">
<li
slot="list-footer"
class="pagination"
>
<button
v-on:click.prevent="
@click.prevent="
$event.preventDefault();
page -= 1;
"
@ -226,7 +291,7 @@
Précédent
</button>
<button
v-on:click.prevent="
@click.prevent="
$event.preventDefault();
page += 1;
"
@ -239,7 +304,10 @@
</VueSelect>
</div>
<div class="form-group mb-3">
<label for="text" class="col-form-label text-md-right ms-5">
<label
for="text"
class="col-form-label text-md-right ms-5"
>
Description courte
</label>
<textarea
@ -251,18 +319,21 @@
placeholder=""
@keydown="limitLines($event)"
v-model="modale.text"
></textarea>
/>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-8 text-center">
<button class="btn btn-primary me-3" v-on:click.prevent="saveCell()">
<button
class="btn btn-primary me-3"
@click.prevent="saveCell()"
>
Valider
</button>
<button
class="btn btn-danger"
v-on:click.prevent="($refs.close as HTMLButtonElement).click()"
@click.prevent="($refs.close as HTMLButtonElement).click()"
>
Fermer
</button>
@ -359,10 +430,11 @@ export default defineComponent({
window.Toastify = Toastify;
const json = String(this.$attrs.json ?? "{}"),
data = JSON.parse(json);
data = JSON.parse(json);
this.daySlots =
Number.parseInt(data.daySlots) !== NaN ? Number.parseInt(data.daySlots) : 4;
this.daySlots = Number.isNaN(Number.parseInt(data.daySlots))
? Number.parseInt(data.daySlots)
: 4;
this.showWeekends = data.daySlots == "true" ? true : false;
this.currentDate = data.browseDate ?? new Date();
this.weeksCells = data.weeks;
@ -530,7 +602,9 @@ export default defineComponent({
throw new Error("undefined route schedule.index");
}
// console.log(forward);
let date = this.momentJs(this.currentDate).startOf("week").subtract(7, "day");
let date = this.momentJs(this.currentDate)
.startOf("week")
.subtract(7, "day");
if (forward) {
date = this.momentJs(this.currentDate).add(7, "day");
}
@ -542,12 +616,12 @@ export default defineComponent({
let mydays = [];
for (let i = 0; i < this.numweeks * 7; i++) {
const date = this.momentJs(this.currentDate).add(i, "day"),
obj = {
day: date.format("ddd"),
num: date.format("DD"),
eday: date.locale("en").format("ddd"),
key: date.format("DD/MM/YYYY"),
};
obj = {
day: date.format("ddd"),
num: date.format("DD"),
eday: date.locale("en").format("ddd"),
key: date.format("DD/MM/YYYY"),
};
mydays.push(obj);
if (!this.showWeekends) {
if (date.day() == 5) {
@ -606,19 +680,32 @@ export default defineComponent({
},
getCellObjectInfo(cellData: DaySlot) {
for (let model of this.models) {
if (cellData["model_lie"].value === model.value && model.value == "tickets") {
if (
cellData["model_lie"].value === model.value &&
model.value == "tickets"
) {
return {
url: this.route("ticket.show", { TICKET: String(cellData["id_lie"].value) }),
url: this.route("ticket.show", {
TICKET: String(cellData["id_lie"].value),
}),
text: cellData["model_lie"].label,
};
}
if (cellData["model_lie"].value === model.value && model.value == "sites") {
if (
cellData["model_lie"].value === model.value &&
model.value == "sites"
) {
return {
url: this.route("sites.show", { SITE: String(cellData["id_lie"].value) }),
url: this.route("sites.show", {
SITE: String(cellData["id_lie"].value),
}),
text: cellData["model_lie"].label,
};
}
if (cellData["model_lie"].value === model.value && model.value == "projects") {
if (
cellData["model_lie"].value === model.value &&
model.value == "projects"
) {
return {
url: this.route("projects.show", {
PROJECT: String(cellData["id_lie"].value),
@ -639,24 +726,24 @@ export default defineComponent({
},
setModalData(userid: number, slotkey: number, day: string, id: number) {
let text = this.schedules[userid][slotkey][day as any]["text"],
activity = this.schedules[userid][slotkey][day as any]["type"],
model = this.schedules[userid][slotkey][day as any]["model_lie"],
enumobject = {
label: "",
value: "",
},
currentModale: Modale = {
id: this.schedules[userid][slotkey][day as any]["id"],
userid: userid,
slot: slotkey,
day: day,
title: String(id),
dDisplay: false,
text: text,
activity: activity,
model_lie: model ?? enumobject,
id_lie: this.schedules[userid][slotkey][day as any]["id_lie"],
};
activity = this.schedules[userid][slotkey][day as any]["type"],
model = this.schedules[userid][slotkey][day as any]["model_lie"],
enumobject = {
label: "",
value: "",
},
currentModale: Modale = {
id: this.schedules[userid][slotkey][day as any]["id"],
userid: userid,
slot: slotkey,
day: day,
title: String(id),
dDisplay: false,
text: text,
activity: activity,
model_lie: model ?? enumobject,
id_lie: this.schedules[userid][slotkey][day as any]["id_lie"],
};
this.modale = currentModale;
this.$nextTick(() => {
if (this.modale.id_lie.value === null) {
@ -664,26 +751,26 @@ export default defineComponent({
}
let route = null;
switch (this.modale.model_lie.value) {
case "tickets":
this.modale.dDisplay = true;
route = this.route("ticket.show", {
TICKET: this.modale.id_lie.value,
});
break;
case "projects":
this.modale.dDisplay = true;
route = this.route("projects.show", {
PROJECT: this.modale.id_lie.value,
});
break;
case "sites":
this.modale.dDisplay = true;
route = this.route("sites.show", {
SITE: this.modale.id_lie.value,
});
break;
default:
this.modale.dDisplay = false;
case "tickets":
this.modale.dDisplay = true;
route = this.route("ticket.show", {
TICKET: this.modale.id_lie.value,
});
break;
case "projects":
this.modale.dDisplay = true;
route = this.route("projects.show", {
PROJECT: this.modale.id_lie.value,
});
break;
case "sites":
this.modale.dDisplay = true;
route = this.route("sites.show", {
SITE: this.modale.id_lie.value,
});
break;
default:
this.modale.dDisplay = false;
}
this.selectChange();
this.value = this.schedules[userid][slotkey][day as any]["id_lie"];
@ -770,15 +857,15 @@ export default defineComponent({
this.error = "";
let route;
switch ((this.$refs.morph as HTMLSelectElement).value) {
case "tickets":
route = this.route("ticket.index");
break;
case "projects":
route = this.route("projects.index");
break;
case "sites":
route = this.route("sites.index");
break;
case "tickets":
route = this.route("ticket.index");
break;
case "projects":
route = this.route("projects.index");
break;
case "sites":
route = this.route("sites.index");
break;
}
if (route === null || route === undefined) {
throw new Error(
@ -830,7 +917,7 @@ export default defineComponent({
saveCellOnServer(data: ServerData) {
this.error = "";
const self = this,
routeUri = self.route("schedule.setCellContent");
routeUri = self.route("schedule.setCellContent");
if (self.error.length) {
return;
}
@ -890,9 +977,9 @@ export default defineComponent({
},
eraseCell(userid: number, slotkey: number, day: string, id: number) {
const route = this.route("schedule.eraseCellContent"),
data = {
id: id,
};
data = {
id: id,
};
if (!route) {
throw new Error("undefined route schedule.eraseCellContent");
}
@ -900,7 +987,8 @@ export default defineComponent({
.post(route, data)
.then((response) => {
if (response.data.status) {
this.schedules[userid][slotkey][day as any] = this.emptyCell as DaySlot;
this.schedules[userid][slotkey][day as any] = this
.emptyCell as DaySlot;
} else {
console.error("Unknown response");
}
@ -918,20 +1006,20 @@ export default defineComponent({
value: null,
};
switch ((this.$refs.morph as HTMLSelectElement).value) {
case "tickets":
case "projects":
case "sites":
this.modale.dDisplay = true;
this.search(false);
break;
default:
this.modale.dDisplay = false;
case "tickets":
case "projects":
case "sites":
this.modale.dDisplay = true;
this.search(false);
break;
default:
this.modale.dDisplay = false;
}
},
limitLines(e: KeyboardEvent) {
const comment = this.$refs.comment as HTMLTextAreaElement;
let keynum,
lines = comment.value.split("\n").length;
lines = comment.value.split("\n").length;
if (e.key !== undefined) {
keynum = e.key;
} else if (e.keyCode !== undefined) {

View file

@ -19,7 +19,7 @@ window.addEventListener("DOMContentLoaded", () => {
"Voulez-vous vraiment supprimer cet élément ?"
),
el as HTMLFormElement,
function (response: SweetAlertResult<any>) {
function () {
(el as HTMLFormElement).submit();
},
{ icon: "error" }
@ -28,7 +28,7 @@ window.addEventListener("DOMContentLoaded", () => {
});
}
elements = document.getElementsByClassName("confirmDoneJS");
for (let element of elements) {
for (const element of elements) {
element.addEventListener("submit", function (e) {
e.preventDefault();
const el = e.target;
@ -43,7 +43,7 @@ window.addEventListener("DOMContentLoaded", () => {
"Voulez-vous vraiment marquer cet élément comme traité ?"
),
el as HTMLFormElement,
function (response: SweetAlertResult<any>) {
function () {
(el as HTMLFormElement).submit();
},
{ icon: "info" }
@ -52,7 +52,7 @@ window.addEventListener("DOMContentLoaded", () => {
});
}
elements = document.getElementsByClassName("confirmSubmitJS");
for (let element of elements) {
for (const element of elements) {
element.addEventListener("submit", function (e) {
e.preventDefault();
const el = e.target;
@ -67,7 +67,7 @@ window.addEventListener("DOMContentLoaded", () => {
"Voulez-vous enregistrer cet élément tel-quel ?"
),
el as HTMLFormElement,
function (response: SweetAlertResult<any>) {
function () {
(el as HTMLFormElement).submit();
},
{ icon: "warning" }
@ -76,7 +76,7 @@ window.addEventListener("DOMContentLoaded", () => {
});
}
elements = document.getElementsByClassName("confirmJS");
for (let element of elements) {
for (const element of elements) {
element.addEventListener(
"click",
function (e) {
@ -90,13 +90,12 @@ window.addEventListener("DOMContentLoaded", () => {
}
(async () => {
const promise = new Promise(
(resolve: (value: boolean) => void, reject) => {
console.log(el);
(resolve: (value: boolean) => void) => {
confirm.methods.confirm(
trans.methods.__("Confirmez"),
trans.methods.__("Êtes vous sure ?"),
el,
function (response: SweetAlertResult<any>) {
function (response: SweetAlertResult) {
resolve(response.isConfirmed);
},
{ icon: "warning" }

View file

@ -1,4 +1,4 @@
import { dom, library, config, icon } from "@fortawesome/fontawesome-svg-core";
import { dom, library, config } from "@fortawesome/fontawesome-svg-core";
import {
faTags,
faSquarePlus,
@ -17,9 +17,11 @@ import {
faFileImage,
faInbox,
faArrowLeftLong,
faPlus,
faMinus,
} from "@fortawesome/free-solid-svg-icons";
import { faAccessibleIcon } from "@fortawesome/free-brands-svg-icons";
import { faCalendarAlt } from "@fortawesome/free-regular-svg-icons";
// import { faAccessibleIcon } from "@fortawesome/free-brands-svg-icons";
// import { faCalendarAlt } from "@fortawesome/free-regular-svg-icons";
config.autoReplaceSvg = false;
library.add(faTags);
@ -39,4 +41,6 @@ library.add(faLock);
library.add(faFileImage);
library.add(faInbox);
library.add(faArrowLeftLong);
library.add(faPlus);
library.add(faMinus);
dom.watch();

View file

@ -1,5 +1,6 @@
export {};
import * as Swal from "sweetalert2";
import { AxiosStatic } from "axios";
import type { VueSelectProps } from "vue-select";
@ -10,13 +11,20 @@ type SelectModelType = {
value: string;
};
type EnumType = {
label: string;
value: number | string;
};
// * Recursive List<string>
type StringList = Record<PropertyKey, string>;
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface NestedStringListInterface
extends Record<PropertyKey, NestedStringListInterface | StringList> {}
// * Recursive List<number>
type NumberList = Record<PropertyKey, number>;
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface NestedNumberListInterface
extends Record<PropertyKey, NestedNumberListInterface | NumberList> {}
@ -31,6 +39,7 @@ type Model = Record<
| NestedNumberList
| NestedModelInterface
>;
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface NestedModelInterface extends Model {}
type NestedStringList = NestedStringListInterface | StringList | string;
@ -44,13 +53,14 @@ declare global {
type VSelectProps = VueSelectProps;
type SelectModel = SelectModelType;
type Enum = EnumType;
interface Window {
$displayMenu: (show: boolean) => void;
$toggleBasketMenu: () => void;
axios: AxiosStatic;
Swal: Function;
__SESSION_WATCH: any;
Swal: typeof Swal;
__SESSION_WATCH: Record<string, unknown>;
__SYSTEM: {
_asset: string;
_locale: string;

View file

@ -8,20 +8,18 @@ export default {
methods: {
confirm(
title: string,
text: string = "",
self: Object,
after: (response: SweetAlertResult<any>) => void = (
response: SweetAlertResult<any>
) => {},
text = "",
self: HTMLElement,
after: (response: SweetAlertResult) => void,
options: SweetAlertOptions = {}
) {
if (!self) {
throw Error('self is needed to set "this" on callback');
throw Error("self is needed to set 'this' on callback");
}
if (!after) {
throw Error("callback is needed when using confirm");
}
let icon = options.icon ?? "warning";
const icon = options.icon ?? "warning";
Swal.fire({
title: title,
text: text,

View file

@ -7,7 +7,7 @@ const route = {
const routes = route.flattenProperties(window.__SYSTEM._routes);
const routeUrl =
Object.keys(routes).indexOf(routeName) !== -1
? routes[routeName]
? routes[routeName] as string
: null;
if (!routeUrl) {
return routeUrl;
@ -31,8 +31,8 @@ const route = {
delete parameters[paramName];
}
for (let paramName in parameters) {
let paramValue = parameters[paramName];
for (const paramName in parameters) {
const paramValue = parameters[paramName];
if (search.indexOf(paramName + "=") >= 0) {
const prefix = search.substring(
0,
@ -57,14 +57,18 @@ const route = {
},
},
flattenProperties(
obj: Record<PropertyKey, any>,
obj: Record<PropertyKey, unknown>,
parent: string | null = null,
res: Record<PropertyKey, any> = {}
): Record<PropertyKey, any> {
res: Record<PropertyKey, unknown> = {}
): Record<PropertyKey, unknown> {
for (const key of Object.keys(obj)) {
const propName = parent ? parent + "." + key : key;
if (typeof obj[key] === "object") {
route.flattenProperties(obj[key], propName, res);
if (obj[key] instanceof Object) {
route.flattenProperties(
obj[key] as Record<PropertyKey, unknown>,
propName,
res
);
} else {
res[propName] = obj[key];
}

View file

@ -16,7 +16,7 @@ export default {
translation = key
.split(".")
.reduce(
(t, i) => t[i as any] ?? null,
(t, i) => t[i as never] ?? null,
window.__SYSTEM._translations.php
) as string;
if (translation) {
@ -27,8 +27,9 @@ export default {
}
if (translationNotFound) {
translation = (window.__SYSTEM._translations.json[key as any] ??
key) as string;
translation = (window.__SYSTEM._translations.json[
key as never
] ?? key) as string;
}
const vars = translation.match(/:[0-9A-Za-z_]+/g);
if (vars) {

View file

@ -1,45 +1,50 @@
document.addEventListener("DOMContentLoaded", () => {
// Page Project File Add
if (document.querySelector(".content.pageFile")) {
let selectFileType = document.getElementById(
"type"
) as HTMLSelectElement,
inputUri = document.getElementById("uri") as HTMLInputElement,
inputFile = document.getElementById("file") as HTMLInputElement;
let changeFields = () => {
if (
!selectFileType ||
!inputUri ||
!inputUri.parentElement ||
!inputUri.parentElement.parentElement ||
!inputFile ||
!inputFile.parentElement ||
!inputFile.parentElement.parentElement
) {
throw new Error(
"Missing selectFileType or inputUri or inputFile on file page"
);
}
if (selectFileType.value === "0") {
inputUri.setAttribute("required", "");
inputUri.removeAttribute("disabled");
inputUri.parentElement.parentElement.classList.remove("d-none");
inputFile.removeAttribute("required");
inputFile.setAttribute("disabled", "");
inputFile.parentElement.parentElement.classList.add("d-none");
} else {
inputUri.removeAttribute("required");
inputUri.removeAttribute("required");
inputUri.setAttribute("disabled", "");
inputUri.parentElement.parentElement.classList.add("d-none");
inputFile.setAttribute("required", "");
inputFile.removeAttribute("disabled");
inputFile.parentElement.parentElement.classList.remove(
"d-none"
);
}
};
const selectFileType = document.getElementById(
"type"
) as HTMLSelectElement;
const inputUri = document.getElementById("uri") as HTMLInputElement,
inputFile = document.getElementById("file") as HTMLInputElement,
changeFields = () => {
if (
!selectFileType ||
!inputUri ||
!inputUri.parentElement ||
!inputUri.parentElement.parentElement ||
!inputFile ||
!inputFile.parentElement ||
!inputFile.parentElement.parentElement
) {
throw new Error(
"Missing selectFileType or inputUri or inputFile on file page"
);
}
if (selectFileType.value === "0") {
inputUri.setAttribute("required", "");
inputUri.removeAttribute("disabled");
inputUri.parentElement.parentElement.classList.remove(
"d-none"
);
inputFile.removeAttribute("required");
inputFile.setAttribute("disabled", "");
inputFile.parentElement.parentElement.classList.add(
"d-none"
);
} else {
inputUri.removeAttribute("required");
inputUri.removeAttribute("required");
inputUri.setAttribute("disabled", "");
inputUri.parentElement.parentElement.classList.add(
"d-none"
);
inputFile.setAttribute("required", "");
inputFile.removeAttribute("disabled");
inputFile.parentElement.parentElement.classList.remove(
"d-none"
);
}
};
changeFields();
selectFileType.addEventListener("change", changeFields);

View file

@ -6,9 +6,9 @@ document.addEventListener("DOMContentLoaded", () => {
document.querySelector(".content.ticketsIndex") ||
document.querySelector(".content.ticketsShow")
) {
let labels = document.querySelectorAll(".label.colorize");
const labels = document.querySelectorAll(".label.colorize");
labels.forEach((label, key) => {
labels.forEach((label) => {
if (!(label instanceof HTMLElement)) {
throw new Error("label has to be a HTMLElement");
}

View file

@ -1,10 +1,10 @@
import Swal from "sweetalert2";
import trans from "./modules/trans";
import validateSession from "./modules/session_pinger";
document.addEventListener("DOMContentLoaded", () => {
let validateSession = require("./modules/session_pinger").default.methods
.validateSession,
sessionInvalidAction = () => {
let timeOut: ReturnType<typeof setTimeout> | undefined = undefined;
const sessionInvalidAction = () => {
Swal.fire({
title: trans.methods.__("Session expirée"),
text: trans.methods.__(
@ -20,9 +20,8 @@ document.addEventListener("DOMContentLoaded", () => {
clearTimeout(timeOut);
},
timer = 10000,
timeOut: NodeJS.Timeout | undefined,
timerAction = () => {
validateSession(sessionInvalidAction);
validateSession.methods.validateSession(sessionInvalidAction);
timeOut = setTimeout(timerAction, timer);
};

View file

@ -21,7 +21,7 @@ document.addEventListener("DOMContentLoaded", () => {
.replace(/^-/, "")
.replace("-component", "");
let el = document.getElementById(htmlKey),
const el = document.getElementById(htmlKey),
elGroup = document.getElementsByClassName(htmlKey);
// * Init using ID
if (el) {

View file

@ -1,9 +1,7 @@
{
"compilerOptions": {
"target": "esnext",
"lib": [
"es5", "es6", "dom", "dom.iterable"
],
"lib": ["es5", "es6", "dom", "dom.iterable"],
"module": "esnext",
// "isolatedModules": false,
// "experimentalDecorators": true,
@ -15,12 +13,9 @@
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"typeRoots": ["resources/ts/@types", "node_modules/@types"],
"typeRoots": ["resources/ts/@types", "node_modules/@types"]
},
"include": [
"resources/**/*.js",
"resources/**/*.ts",
"resources/**/*.vue"
],
"include": ["resources/**/*.js", "resources/**/*.ts", "resources/**/*.vue"],
"exclude": ["node_modules"],
"compileOnSave": false
}

65
webpack.mix.js vendored
View file

@ -1,40 +1,41 @@
const path = require('path')
const mix = require('laravel-mix')
const path = require("path");
const mix = require("laravel-mix");
require('laravel-mix-polyfill')
require('laravel-mix-clean')
require("laravel-mix-polyfill");
require("laravel-mix-clean");
require("laravel-mix-eslint");
const assetDir = 'assets'
const assetDir = "assets";
const polyfill = {
enabled: true,
bugfixes: true,
useBuiltIns: "usage",
targets: 'last 3 version, not dead, >0.3%'
}
targets: "last 3 version, not dead, >0.3%",
};
mix.webpackConfig({
resolve: {
extensions: ["*", ".js", ".vue", ".ts"],
alias: {
'@': path.resolve('resources/assets'),
'vue$': 'vue/dist/vue.runtime.esm-bundler.js',
}
"@": path.resolve("resources/assets"),
vue$: "vue/dist/vue.runtime.esm-bundler.js",
},
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader',
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.vue$/],
}
},
},
{
test: /\.vue$/,
loader: 'vue-loader',
loader: "vue-loader",
options: {
esModule: true
}
esModule: true,
},
},
],
},
@ -45,39 +46,39 @@ mix.webpackConfig({
// /\-\-underline\-color/,
// ]
// },
})
});
mix.options({
fileLoaderDirs: {
fonts: `${assetDir}/fonts`,
images: `${assetDir}/images`
images: `${assetDir}/images`,
},
postCss: [
require('postcss-discard-comments')({
removeAll: true
})
require("postcss-discard-comments")({
removeAll: true,
}),
],
})
});
mix.clean({
cleanOnceBeforeBuildPatterns: [
'css/*',
'js/*',
'assets/*',
]
})
cleanOnceBeforeBuildPatterns: ["css/*", "js/*", "assets/*"],
});
// mix Backend
mix.ts('resources/ts/app.ts', 'public/js/app.js', { transpileOnly: true })
mix.ts("resources/ts/app.ts", "public/js/app.js", { transpileOnly: true })
.vue({
runtimeOnly: true,
extractStyles: true,
globalStyles: false
globalStyles: false,
})
.sass('resources/sass/app.scss', 'public/css')
.eslint({
fix: true,
extensions: ["js", "ts"],
})
.sass("resources/sass/app.scss", "public/css")
.polyfill(polyfill)
.version()
.sourceMaps(true, 'inline-source-map')
.sourceMaps(true, "inline-source-map");
// Copy Blade Images Assets
mix.copyDirectory('resources/assets', 'public/assets')
mix.copyDirectory("resources/assets", "public/assets");