ldc-supportweb/resources/ts/bo/components/InvoiceTasksComponent.vue
2023-11-14 09:37:49 +01:00

316 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>
<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.125"
min="0"
>
</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) }}&nbsp;€
</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) }}&nbsp;€</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>