ResponsibleDialog.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <template>
  2. <q-dialog ref="dialogRef" @hide="onDialogHide">
  3. <q-card
  4. class="q-dialog-plugin dialog-form-card"
  5. style="width: 100%; max-width: 900px"
  6. >
  7. <DefaultDialogHeader
  8. :title="props.responsible ? 'Editar Responsável' : 'Novo Responsável'"
  9. @close="onDialogCancel"
  10. />
  11. <q-scroll-area class="dialog-form-scroll" style="height: 65vh">
  12. <q-card-section class="q-pt-sm">
  13. <DefaultForm ref="formRef">
  14. <div class="row q-col-gutter-sm">
  15. <DefaultInput
  16. v-model="form.name"
  17. :error="!!validationErrors.name"
  18. :error-message="validationErrors.name"
  19. label="Nome"
  20. class="col-6"
  21. :rules="[inputRules.required]"
  22. />
  23. <DefaultInput
  24. v-model="form.degree"
  25. :error="!!validationErrors.degree"
  26. :error-message="validationErrors.degree"
  27. label="Grau de Parentesco"
  28. class="col-6"
  29. :rules="[inputRules.required]"
  30. />
  31. <DefaultInputDatePicker
  32. v-model="form.birth_date"
  33. :error="!!validationErrors.birth_date"
  34. :error-message="validationErrors.birth_date"
  35. label="Data de Nascimento"
  36. class="col-4"
  37. :rules="[inputRules.required]"
  38. />
  39. <DefaultInput
  40. v-model="form.cpf"
  41. :error="!!validationErrors.cpf"
  42. :error-message="validationErrors.cpf"
  43. label="CPF"
  44. class="col-4"
  45. :mask="masks.Brasil.cpf"
  46. :rules="[inputRules.required, inputRules.cpf]"
  47. />
  48. <DefaultSelect
  49. v-model="form.gender"
  50. :error="!!validationErrors.gender"
  51. :error-message="validationErrors.gender"
  52. label="Gênero"
  53. class="col-4"
  54. emit-value
  55. map-options
  56. :options="genderOptions"
  57. />
  58. <DefaultInput
  59. v-model="form.email"
  60. :error="!!validationErrors.email"
  61. :error-message="validationErrors.email"
  62. label="E-mail"
  63. class="col-6"
  64. type="email"
  65. :rules="[inputRules.email]"
  66. />
  67. <DefaultInput
  68. v-model="form.phone"
  69. :error="!!validationErrors.phone"
  70. :error-message="validationErrors.phone"
  71. label="Telefone"
  72. class="col-6"
  73. :mask="masks.Brasil.celular"
  74. :rules="[inputRules.required]"
  75. />
  76. <DefaultCepInput
  77. v-model="form.postal_code"
  78. :error="!!validationErrors.postal_code"
  79. :error-message="validationErrors.postal_code"
  80. class="col-4"
  81. @rua="(v) => (form.street = v)"
  82. @bairro="(v) => (form.neighborhood = v)"
  83. @uf="(v) => stateSelectRef?.selectStateByCode(v)"
  84. @cidade="(v) => citySelectRef?.selectCityByName(v)"
  85. />
  86. <DefaultInput
  87. v-model="form.street"
  88. :error="!!validationErrors.street"
  89. :error-message="validationErrors.street"
  90. label="Endereço"
  91. class="col-5"
  92. />
  93. <DefaultInput
  94. v-model="form.address_number"
  95. :error="!!validationErrors.address_number"
  96. :error-message="validationErrors.address_number"
  97. label="Número"
  98. class="col-3"
  99. />
  100. <DefaultInput
  101. v-model="form.neighborhood"
  102. :error="!!validationErrors.neighborhood"
  103. :error-message="validationErrors.neighborhood"
  104. label="Bairro"
  105. class="col-4"
  106. />
  107. <CitySelect
  108. ref="citySelectRef"
  109. v-model="selectedCity"
  110. :error="!!validationErrors.city_id"
  111. :error-message="validationErrors.city_id"
  112. label="Cidade"
  113. class="col-4"
  114. :state="selectedState"
  115. :initial-id="props.responsible?.city_id ?? null"
  116. />
  117. <StateSelect
  118. ref="stateSelectRef"
  119. v-model="selectedState"
  120. :error="!!validationErrors.state_id"
  121. :error-message="validationErrors.state_id"
  122. label="Estado"
  123. class="col-4"
  124. :initial-id="props.responsible?.state_id ?? null"
  125. />
  126. <DefaultInput
  127. v-model="form.complement"
  128. :error="!!validationErrors.complement"
  129. :error-message="validationErrors.complement"
  130. label="Complemento"
  131. class="col-12"
  132. />
  133. <DefaultInput
  134. v-model="form.notes"
  135. :error="!!validationErrors.notes"
  136. :error-message="validationErrors.notes"
  137. label="Observações"
  138. class="col-12"
  139. type="textarea"
  140. :input-style="{ minHeight: '120px' }"
  141. autogrow
  142. />
  143. </div>
  144. </DefaultForm>
  145. </q-card-section>
  146. </q-scroll-area>
  147. <q-separator />
  148. <q-card-actions align="right">
  149. <q-btn
  150. outline
  151. color="primary"
  152. label="CANCELAR"
  153. @click="onDialogCancel"
  154. />
  155. <q-btn
  156. color="primary"
  157. label="SALVAR"
  158. :loading="saving"
  159. @click="handleSave"
  160. />
  161. </q-card-actions>
  162. </q-card>
  163. </q-dialog>
  164. </template>
  165. <script setup>
  166. import { ref, watch, useTemplateRef } from "vue";
  167. import { useDialogPluginComponent } from "quasar";
  168. import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
  169. import DefaultInput from "src/components/defaults/DefaultInput.vue";
  170. import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
  171. import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
  172. import DefaultCepInput from "src/components/defaults/DefaultCepInput.vue";
  173. import CitySelect from "src/components/selects/CitySelect.vue";
  174. import StateSelect from "src/components/selects/StateSelect.vue";
  175. import { useInputRules } from "src/composables/useInputRules";
  176. import { useSubmitHandler } from "src/composables/useSubmitHandler";
  177. import {
  178. createStudentResponsible,
  179. updateStudentResponsible,
  180. } from "src/api/studentResponsible";
  181. import masks from "src/helpers/masks";
  182. import { formatDateYMDtoDMY, formatDateDMYtoYMD } from "src/helpers/utils";
  183. const props = defineProps({
  184. studentId: {
  185. type: Number,
  186. required: true,
  187. },
  188. responsible: {
  189. type: Object,
  190. default: null,
  191. },
  192. });
  193. defineEmits([...useDialogPluginComponent.emits]);
  194. const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
  195. useDialogPluginComponent();
  196. const { inputRules } = useInputRules();
  197. const formRef = useTemplateRef("formRef");
  198. const stateSelectRef = useTemplateRef("stateSelectRef");
  199. const citySelectRef = useTemplateRef("citySelectRef");
  200. const selectedState = ref(null);
  201. const selectedCity = ref(null);
  202. const genderOptions = [
  203. { label: "Masculino", value: "male" },
  204. { label: "Feminino", value: "female" },
  205. { label: "Outro", value: "other" },
  206. { label: "Prefiro não informar", value: "no_preference" },
  207. ];
  208. const form = ref(
  209. props.responsible
  210. ? {
  211. name: props.responsible.name ?? null,
  212. birth_date: props.responsible.birth_date
  213. ? formatDateYMDtoDMY(props.responsible.birth_date)
  214. : null,
  215. cpf: props.responsible.cpf ?? null,
  216. gender: props.responsible.gender ?? "no_preference",
  217. degree: props.responsible.degree ?? null,
  218. email: props.responsible.email ?? null,
  219. phone: props.responsible.phone ?? null,
  220. postal_code: props.responsible.postal_code ?? null,
  221. street: props.responsible.street ?? null,
  222. address_number: props.responsible.address_number ?? null,
  223. neighborhood: props.responsible.neighborhood ?? null,
  224. state_id: props.responsible.state_id ?? null,
  225. city_id: props.responsible.city_id ?? null,
  226. complement: props.responsible.complement ?? null,
  227. notes: props.responsible.notes ?? null,
  228. }
  229. : {
  230. name: null,
  231. birth_date: null,
  232. cpf: null,
  233. gender: "no_preference",
  234. degree: null,
  235. email: null,
  236. phone: null,
  237. postal_code: null,
  238. street: null,
  239. address_number: null,
  240. neighborhood: null,
  241. state_id: null,
  242. city_id: null,
  243. complement: null,
  244. notes: null,
  245. },
  246. );
  247. watch(selectedState, (state) => {
  248. form.value.state_id = state?.value ?? null;
  249. });
  250. watch(selectedCity, (city) => {
  251. form.value.city_id = city?.value ?? null;
  252. });
  253. const { loading: saving, validationErrors, execute } = useSubmitHandler({
  254. formRef,
  255. onSuccess: () => onDialogOK(true),
  256. });
  257. async function handleSave() {
  258. const valid = await formRef.value?.validate();
  259. if (!valid) return;
  260. const payload = {
  261. student_id: props.studentId,
  262. name: form.value.name,
  263. birth_date: form.value.birth_date
  264. ? formatDateDMYtoYMD(form.value.birth_date)
  265. : null,
  266. cpf: form.value.cpf,
  267. gender: form.value.gender,
  268. degree: form.value.degree,
  269. email: form.value.email,
  270. phone: form.value.phone,
  271. postal_code: form.value.postal_code,
  272. street: form.value.street,
  273. address_number: form.value.address_number,
  274. neighborhood: form.value.neighborhood,
  275. city_id: form.value.city_id,
  276. state_id: form.value.state_id,
  277. complement: form.value.complement,
  278. notes: form.value.notes,
  279. };
  280. if (props.responsible) {
  281. await execute(() =>
  282. updateStudentResponsible(props.responsible.id, payload),
  283. );
  284. } else {
  285. await execute(() => createStudentResponsible(payload));
  286. }
  287. }
  288. </script>