AddEditStudentDialog.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <template>
  2. <q-dialog ref="dialogRef" @hide="onDialogHide">
  3. <q-card
  4. class="q-dialog-plugin overflow-hidden"
  5. style="width: 900px; max-width: 95vw"
  6. >
  7. <DefaultDialogHeader title="Cadastro de Aluno" @close="onDialogCancel" />
  8. <q-form ref="formRef" @submit="onOKClick">
  9. <q-card-section class="q-pt-none">
  10. <div class="text-subtitle2 q-mb-sm">Dados do Aluno</div>
  11. <div class="row q-col-gutter-sm">
  12. <DefaultInput
  13. v-model="form.name"
  14. label="Nome do aluno"
  15. class="col-md-5 col-12"
  16. :rules="[inputRules.required]"
  17. />
  18. <DefaultInputDatePicker
  19. v-model="form.birthdate"
  20. label="Data de Nascimento"
  21. class="col-md-5 col-12"
  22. />
  23. <div class="col-md-2 col-12 flex justify-center items-start">
  24. <div style="position: relative; display: inline-block">
  25. <q-avatar size="72px" color="grey-3">
  26. <img v-if="avatarPreview" :src="avatarPreview" />
  27. <q-icon
  28. v-else
  29. name="mdi-account"
  30. size="42px"
  31. color="grey-6"
  32. />
  33. </q-avatar>
  34. <q-btn
  35. round
  36. dense
  37. color="primary"
  38. icon="mdi-camera"
  39. size="xs"
  40. style="position: absolute; bottom: 0; right: 0"
  41. @click="triggerFileInput"
  42. />
  43. <input
  44. ref="fileInputRef"
  45. type="file"
  46. accept="image/*"
  47. style="display: none"
  48. @change="onAvatarChange"
  49. />
  50. </div>
  51. </div>
  52. <DefaultInput
  53. v-model="form.cpf"
  54. label="CPF / CNH"
  55. class="col-md-6 col-12"
  56. :mask="masks.Brasil.cpf"
  57. :rules="[inputRules.cpf]"
  58. />
  59. <DefaultSelect
  60. v-model="form.gender"
  61. label="Gênero"
  62. class="col-md-6 col-12"
  63. emit-value
  64. map-options
  65. :options="genderOptions"
  66. />
  67. <DefaultInput
  68. v-model="form.email"
  69. label="E-mail"
  70. class="col-md-6 col-12"
  71. type="email"
  72. :rules="[inputRules.email]"
  73. />
  74. <DefaultInput
  75. v-model="form.phone"
  76. label="Celular com DDD"
  77. class="col-md-6 col-12"
  78. :mask="masks.Brasil.celular"
  79. />
  80. <DefaultInput
  81. v-model="form.cep"
  82. label="CEP"
  83. class="col-md-3 col-12"
  84. :mask="masks.Brasil.cep"
  85. :rules="[inputRules.cep]"
  86. />
  87. <DefaultInput
  88. v-model="form.address"
  89. label="Endereço"
  90. class="col-md-6 col-12"
  91. />
  92. <DefaultInput
  93. v-model="form.address_number"
  94. label="Número"
  95. class="col-md-3 col-12"
  96. />
  97. <DefaultInput
  98. v-model="form.neighborhood"
  99. label="Bairro"
  100. class="col-md-6 col-12"
  101. />
  102. <StateSelect
  103. v-model="form.state"
  104. label="Estado"
  105. class="col-md-6 col-12"
  106. outlined
  107. />
  108. <DefaultInput
  109. v-model="form.complement"
  110. label="Complemento"
  111. class="col-md-6 col-12"
  112. />
  113. <DefaultInput
  114. v-model="form.payer"
  115. label="Pagador"
  116. class="col-md-6 col-12"
  117. />
  118. <DefaultSelect
  119. v-model="form.how_found"
  120. label="Como nos conheceu?"
  121. class="col-12"
  122. emit-value
  123. map-options
  124. :options="howFoundOptions"
  125. />
  126. <DefaultInput
  127. v-model="form.notes"
  128. label="Observações"
  129. class="col-12"
  130. type="textarea"
  131. autogrow
  132. />
  133. </div>
  134. </q-card-section>
  135. <q-card-actions align="right">
  136. <q-btn
  137. outline
  138. color="primary"
  139. label="CANCELAR"
  140. @click="onDialogCancel"
  141. />
  142. <q-btn
  143. color="primary"
  144. label="CADASTRAR"
  145. type="submit"
  146. :loading="loading"
  147. />
  148. </q-card-actions>
  149. </q-form>
  150. </q-card>
  151. </q-dialog>
  152. </template>
  153. <script setup>
  154. import { ref, useTemplateRef } from "vue";
  155. import { useDialogPluginComponent } from "quasar";
  156. import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
  157. import DefaultInput from "src/components/defaults/DefaultInput.vue";
  158. import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
  159. import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
  160. import StateSelect from "src/components/selects/StateSelect.vue";
  161. import { useInputRules } from "src/composables/useInputRules";
  162. import { useSubmitHandler } from "src/composables/useSubmitHandler";
  163. import { createStudent } from "src/api/student";
  164. import masks from "src/helpers/masks";
  165. import { formatDateDMYtoYMD } from "src/helpers/utils";
  166. defineEmits([...useDialogPluginComponent.emits]);
  167. const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
  168. useDialogPluginComponent();
  169. const { inputRules } = useInputRules();
  170. const formRef = useTemplateRef("formRef");
  171. const fileInputRef = useTemplateRef("fileInputRef");
  172. const avatarPreview = ref(null);
  173. const form = ref({
  174. name: null,
  175. birthdate: null,
  176. cpf: null,
  177. gender: "no_preference",
  178. email: null,
  179. phone: null,
  180. cep: null,
  181. address: null,
  182. address_number: null,
  183. neighborhood: null,
  184. state: null,
  185. complement: null,
  186. payer: null,
  187. how_found: null,
  188. notes: null,
  189. });
  190. const genderOptions = ref([
  191. { label: "Prefiro não informar", value: "no_preference" },
  192. { label: "Masculino", value: "male" },
  193. { label: "Feminino", value: "female" },
  194. { label: "Outro", value: "other" },
  195. ]);
  196. const howFoundOptions = ref([
  197. { label: "Indicação", value: "referral" },
  198. { label: "Redes Sociais", value: "social_media" },
  199. { label: "Google", value: "google" },
  200. { label: "Outro", value: "other" },
  201. ]);
  202. const { loading, execute } = useSubmitHandler({
  203. formRef,
  204. onSuccess: () => {
  205. onDialogOK(true);
  206. },
  207. });
  208. function buildPayload() {
  209. return {
  210. name: form.value.name,
  211. birth_date: form.value.birthdate ? formatDateDMYtoYMD(form.value.birthdate) : null,
  212. document_number: form.value.cpf,
  213. gender: form.value.gender,
  214. email: form.value.email || null,
  215. phone: form.value.phone,
  216. postal_code: form.value.cep,
  217. street: form.value.address,
  218. address_number: form.value.address_number,
  219. neighborhood: form.value.neighborhood,
  220. state_id: form.value.state?.value ?? null,
  221. complement: form.value.complement,
  222. payer_name: form.value.payer,
  223. how_did_you_know_us: form.value.how_found,
  224. notes: form.value.notes,
  225. };
  226. }
  227. function triggerFileInput() {
  228. fileInputRef.value?.click();
  229. }
  230. function onAvatarChange(event) {
  231. const file = event.target.files[0];
  232. if (file) {
  233. avatarPreview.value = URL.createObjectURL(file);
  234. }
  235. }
  236. async function onOKClick() {
  237. await execute(() => createStudent(buildPayload()));
  238. }
  239. </script>