ResponsibleDialog.vue 11 KB

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