314 lines
8.6 KiB
Vue
314 lines
8.6 KiB
Vue
<template>
|
|
<div v-if="!companies.length">
|
|
<span class="text-danger">
|
|
{{
|
|
__("Pour utiliser ce composant, il faut des :compagnies", {
|
|
compagnies: __("models.classes.companies"),
|
|
})
|
|
}}
|
|
</span>
|
|
</div>
|
|
<div v-else-if="!packages.length">
|
|
<span class="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 table-hover">
|
|
<thead>
|
|
<th>{{ __("Commentaire") }}</th>
|
|
<th>{{ __("Type") }}</th>
|
|
<th width="100">
|
|
{{ __("Jours") }}
|
|
</th>
|
|
<th width="140">
|
|
{{ __("Montant") }}
|
|
</th>
|
|
</thead>
|
|
<tbody>
|
|
<template v-if="tasks.length">
|
|
<tr
|
|
v-for="(task, index) in tasks"
|
|
:key="index"
|
|
>
|
|
<td>
|
|
<textarea
|
|
v-model="task.desc"
|
|
rows="2"
|
|
class="form-control"
|
|
placeholder="Un beau commentaire"
|
|
/>
|
|
</td>
|
|
<td>
|
|
<select
|
|
v-model="task.type"
|
|
@change="updateAmount(index)"
|
|
class="form-select"
|
|
>
|
|
<option
|
|
v-for="(typeEnum, typeIndex) in taskTypeEnums"
|
|
:value="typeEnum.value"
|
|
:key="typeIndex"
|
|
>
|
|
{{ typeEnum.label }}
|
|
</option>
|
|
</select>
|
|
<select
|
|
v-if="task.type === getTaskTypeEnum('forfait').value"
|
|
v-model="task.packageModel"
|
|
@change="updateAmount(index)"
|
|
class="form-select"
|
|
>
|
|
<option
|
|
v-for="(packageModel, packageIndex) in packages"
|
|
:value="packageModel"
|
|
:key="packageIndex"
|
|
:selected="!task.packageModel && index === 0"
|
|
>
|
|
{{ packageModel.name }}
|
|
</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<input
|
|
class="inputNumber form-control"
|
|
v-model="task.days"
|
|
@change="updateAmount(index)"
|
|
type="number"
|
|
step="0.0625"
|
|
>
|
|
</td>
|
|
<td>
|
|
<div
|
|
v-if="task.type === getTaskTypeEnum('libre').value"
|
|
class="input-group"
|
|
>
|
|
<input
|
|
class="inputNumber form-control"
|
|
v-model="task.amount"
|
|
type="number"
|
|
step="0.01"
|
|
>
|
|
<span class="input-group-text">€</span>
|
|
</div>
|
|
<template v-else>
|
|
{{ task.amount.toFixed(2) }} €
|
|
</template>
|
|
</td>
|
|
<td class="text-end">
|
|
<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>
|
|
</template>
|
|
<tr>
|
|
<td
|
|
colspan="5"
|
|
class="text-end"
|
|
>
|
|
<button
|
|
class="btn btn-sm btn-primary"
|
|
@click.prevent="addTasksRow()"
|
|
>
|
|
<font-awesome-icon icon="fa-solid fa-plus" />
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="2">
|
|
{{ __("Total") }}
|
|
</td>
|
|
<td>
|
|
<span class="mt-2">{{ totalDays }}</span>
|
|
</td>
|
|
<td>
|
|
<span class="mt-2">
|
|
{{ Number.parseFloat(String(total)).toFixed(2) }} €</span>
|
|
</td>
|
|
<td />
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<input
|
|
class="d-none"
|
|
name="tasks"
|
|
type="text"
|
|
:value="JSON.stringify(databaseTasks)"
|
|
>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|
import { defineComponent } from "vue";
|
|
import trans from "../../modules/trans";
|
|
|
|
type TaskRow = {
|
|
desc: string;
|
|
days: number;
|
|
type: number;
|
|
amount: number;
|
|
package: number | null;
|
|
packageModel: LaravelModel | null;
|
|
};
|
|
|
|
type TaskTypeEnum = Enum & { price: number };
|
|
|
|
export default defineComponent({
|
|
mixins: [trans],
|
|
components: {
|
|
FontAwesomeIcon,
|
|
},
|
|
data(): {
|
|
tasks: TaskRow[];
|
|
companies: LaravelModelList;
|
|
packages: LaravelModelList;
|
|
taskTypeEnums: TaskTypeEnum[];
|
|
taskRowModel: TaskRow;
|
|
} {
|
|
return {
|
|
tasks: [],
|
|
companies: [],
|
|
packages: [],
|
|
taskTypeEnums: [],
|
|
taskRowModel: {
|
|
desc: "",
|
|
days: 0,
|
|
type: 1,
|
|
amount: 0,
|
|
package: null,
|
|
packageModel: null,
|
|
},
|
|
};
|
|
},
|
|
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;
|
|
|
|
const tasks = data.tasks;
|
|
tasks.map((task: TaskRow) => {
|
|
if (task.package) {
|
|
task.packageModel =
|
|
data.packages.find(
|
|
(packageModel: LaravelModel) => packageModel.id === task.package
|
|
) ?? null;
|
|
}
|
|
if (task.type !== this.getTaskTypeEnum("forfait").value) {
|
|
task.package = null;
|
|
task.packageModel = null;
|
|
}
|
|
});
|
|
this.tasks = tasks;
|
|
|
|
// * Use first enum value as default
|
|
this.taskRowModel.type = this.getTaskTypeEnum("web").value as number;
|
|
this.taskRowModel.packageModel = this.packages[0];
|
|
},
|
|
computed: {
|
|
databaseTasks(): TaskRow[] {
|
|
let tasks = [...this.tasks];
|
|
tasks.map((task: TaskRow) => {
|
|
task = Object.assign({}, task);
|
|
task.package = task.packageModel ? task.package : null;
|
|
task.packageModel = null;
|
|
});
|
|
return 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: {
|
|
tasks(tasks: TaskRow[]) {
|
|
tasks.map((task) => {
|
|
task.package = task.packageModel?.id as number | null;
|
|
});
|
|
this.tasks = tasks;
|
|
},
|
|
},
|
|
methods: {
|
|
/**
|
|
* Get the coresponding task type using value or label
|
|
*
|
|
* @param type
|
|
*/
|
|
getTaskTypeEnum(type: string | number): TaskTypeEnum {
|
|
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.package = null;
|
|
switch (task.type) {
|
|
case this.getTaskTypeEnum("web").value:
|
|
case this.getTaskTypeEnum("seo").value:
|
|
task.amount = task.days * this.getTaskTypeEnum(task.type).price;
|
|
break;
|
|
case this.getTaskTypeEnum("forfait").value:
|
|
task.amount =
|
|
task.days * ((task.packageModel?.amount ?? 0) as number);
|
|
task.package = (task.packageModel?.id as number) ?? null;
|
|
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>
|