ldc-supportweb/resources/ts/components/InvoiceTasksComponent.vue
2022-08-30 09:22:04 +02:00

273 lines
7.2 KiB
Vue

<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>