AddEditPackageDialog.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <template>
  2. <q-dialog ref="dialogRef" @hide="onDialogHide">
  3. <div style="width: 100%; max-width: 700px">
  4. <q-card class="overflow-hidden" style="width: 100%">
  5. <DefaultDialogHeader
  6. :title="props.package ? 'Editar Pacote' : 'Novo Pacote'"
  7. @close="onDialogCancel"
  8. />
  9. <q-form ref="formRef" @submit="onOKClick">
  10. <q-card-section class="q-pt-sm">
  11. <div class="row q-col-gutter-sm">
  12. <DefaultInput
  13. v-model="form.name"
  14. label="Nome do Pacote"
  15. class="col-12"
  16. />
  17. <DefaultInput
  18. v-model="form.quantity_classes"
  19. label="Quantidade de Aulas"
  20. class="col-12"
  21. type="number"
  22. />
  23. <DefaultCurrencyInput
  24. v-model="form.contract_register_value"
  25. label="R$ Matrícula"
  26. class="col-4"
  27. />
  28. <DefaultCurrencyInput
  29. v-model="form.contract_value"
  30. label="R$ Total do Contrato"
  31. class="col-4"
  32. />
  33. <DefaultInput
  34. v-model="form.contrat_discount_value"
  35. label="Desconto em %"
  36. class="col-4"
  37. type="number"
  38. min="0"
  39. max="100"
  40. />
  41. <div
  42. v-for="(material, index) in form.materials"
  43. :key="index"
  44. class="col-12"
  45. >
  46. <div class="row q-col-gutter-sm items-center">
  47. <DefaultSelect
  48. v-model="material.product_id"
  49. label="Material"
  50. class="col"
  51. :options="productOptions"
  52. emit-value
  53. map-options
  54. @update:model-value="onProductSelected(material)"
  55. />
  56. <DefaultInput
  57. v-model="material.quantity"
  58. label="Qtd"
  59. class="col-2"
  60. type="number"
  61. min="1"
  62. />
  63. <DefaultCurrencyInput
  64. v-model="material.price"
  65. label="R$ Unitário"
  66. class="col-3"
  67. />
  68. <div class="col-auto">
  69. <q-btn
  70. v-if="index === form.materials.length - 1"
  71. color="primary"
  72. icon="mdi-plus"
  73. unelevated
  74. style="border-radius: 8px; height: 40px; width: 40px"
  75. @click="addMaterial"
  76. />
  77. <q-btn
  78. v-else
  79. flat
  80. round
  81. dense
  82. icon="mdi-delete-outline"
  83. color="negative"
  84. @click="removeMaterial(index)"
  85. />
  86. </div>
  87. </div>
  88. </div>
  89. </div>
  90. </q-card-section>
  91. <q-card-actions align="right" class="q-px-md q-pb-md">
  92. <q-btn
  93. outline
  94. color="primary"
  95. label="Cancelar"
  96. @click="onDialogCancel"
  97. />
  98. <q-btn
  99. color="primary"
  100. label="Salvar"
  101. type="submit"
  102. :loading="loading"
  103. />
  104. </q-card-actions>
  105. </q-form>
  106. </q-card>
  107. </div>
  108. </q-dialog>
  109. </template>
  110. <script setup>
  111. import { ref, computed, onMounted } from "vue";
  112. import { useDialogPluginComponent } from "quasar";
  113. import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
  114. import DefaultInput from "src/components/defaults/DefaultInput.vue";
  115. import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
  116. import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.vue";
  117. import {
  118. getUnitPackage,
  119. createUnitPackage,
  120. updateUnitPackage,
  121. } from "src/api/package";
  122. import { getProductsForSelect } from "src/api/product";
  123. defineEmits([...useDialogPluginComponent.emits]);
  124. const props = defineProps({
  125. package: {
  126. type: Object,
  127. default: null,
  128. },
  129. });
  130. const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
  131. useDialogPluginComponent();
  132. const formRef = ref(null);
  133. const loading = ref(false);
  134. const products = ref([]);
  135. const productOptions = computed(() =>
  136. products.value.map((p) => ({
  137. label: p.name,
  138. value: p.id,
  139. price_sale: p.price_sale,
  140. })),
  141. );
  142. const form = ref({
  143. name: props.package?.name ?? null,
  144. quantity_classes: props.package?.quantity_classes ?? null,
  145. contract_register_value: props.package?.contract_register_value ?? null,
  146. contract_value: props.package?.contract_value ?? null,
  147. contrat_discount_value: props.package?.contrat_discount_value ?? null,
  148. materials: [{ product_id: null, quantity: 1, price: null }],
  149. });
  150. const onProductSelected = (material) => {
  151. const option = productOptions.value.find(
  152. (o) => o.value === material.product_id,
  153. );
  154. if (option) material.price = option.price_sale;
  155. };
  156. const addMaterial = () => {
  157. form.value.materials.push({ product_id: null, quantity: 1, price: null });
  158. };
  159. const removeMaterial = (index) => {
  160. form.value.materials.splice(index, 1);
  161. };
  162. onMounted(async () => {
  163. const [allProducts] = await Promise.all([getProductsForSelect()]);
  164. products.value = allProducts;
  165. if (props.package?.id) {
  166. const pkg = await getUnitPackage(props.package.id);
  167. if (pkg.materials?.length) {
  168. form.value.materials = pkg.materials.map((m) => ({
  169. product_id: m.product_id,
  170. quantity: m.quantity,
  171. price: m.price,
  172. }));
  173. }
  174. }
  175. });
  176. const onOKClick = async () => {
  177. loading.value = true;
  178. try {
  179. const validMaterials = form.value.materials.filter((m) => m.product_id);
  180. const payload = {
  181. name: form.value.name,
  182. quantity_classes: form.value.quantity_classes,
  183. contract_value: form.value.contract_value,
  184. contract_register_value: form.value.contract_register_value,
  185. contrat_discount_value: form.value.contrat_discount_value,
  186. materials: validMaterials.map((m) => ({
  187. product_id: m.product_id,
  188. quantity: Number(m.quantity),
  189. price: Number(m.price),
  190. })),
  191. };
  192. const result = props.package?.id
  193. ? await updateUnitPackage(props.package.id, payload)
  194. : await createUnitPackage(payload);
  195. onDialogOK(result);
  196. } finally {
  197. loading.value = false;
  198. }
  199. };
  200. </script>