|
@@ -1,54 +1,34 @@
|
|
|
<template>
|
|
<template>
|
|
|
<q-form ref="formRef" @submit="onSave">
|
|
<q-form ref="formRef" @submit="onSave">
|
|
|
|
|
+ <div class="flex justify-center q-mb-md">
|
|
|
|
|
+ <AvatarImageComponent ref="avatarRef" @update:file="onAvatarChange" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<div class="row q-col-gutter-sm">
|
|
<div class="row q-col-gutter-sm">
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.name"
|
|
v-model="form.name"
|
|
|
- label="Nome do aluno"
|
|
|
|
|
- class="col-md-5 col-12"
|
|
|
|
|
|
|
+ label="Nome do Aluno"
|
|
|
|
|
+ class="col-6"
|
|
|
|
|
+ :rules="[inputRules.required]"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultInputDatePicker
|
|
<DefaultInputDatePicker
|
|
|
v-model="form.birthdate"
|
|
v-model="form.birthdate"
|
|
|
label="Data de Nascimento"
|
|
label="Data de Nascimento"
|
|
|
- class="col-md-5 col-12"
|
|
|
|
|
|
|
+ class="col-6"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
- <div class="col-md-2 col-12 flex justify-center items-start">
|
|
|
|
|
- <div style="position: relative; display: inline-block">
|
|
|
|
|
- <q-avatar size="72px" color="grey-3">
|
|
|
|
|
- <img v-if="avatarPreview" :src="avatarPreview" />
|
|
|
|
|
- <q-icon v-else name="mdi-account" size="42px" color="grey-6" />
|
|
|
|
|
- </q-avatar>
|
|
|
|
|
- <q-btn
|
|
|
|
|
- round
|
|
|
|
|
- dense
|
|
|
|
|
- color="primary"
|
|
|
|
|
- icon="mdi-camera"
|
|
|
|
|
- size="xs"
|
|
|
|
|
- style="position: absolute; bottom: 0; right: 0"
|
|
|
|
|
- @click="triggerFileInput"
|
|
|
|
|
- />
|
|
|
|
|
- <input
|
|
|
|
|
- ref="fileInputRef"
|
|
|
|
|
- type="file"
|
|
|
|
|
- accept="image/*"
|
|
|
|
|
- style="display: none"
|
|
|
|
|
- @change="onAvatarChange"
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.cpf"
|
|
v-model="form.cpf"
|
|
|
label="CPF / CNH"
|
|
label="CPF / CNH"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
|
|
+ class="col-6"
|
|
|
:mask="masks.Brasil.cpf"
|
|
:mask="masks.Brasil.cpf"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultSelect
|
|
<DefaultSelect
|
|
|
v-model="form.gender"
|
|
v-model="form.gender"
|
|
|
label="Gênero"
|
|
label="Gênero"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
|
|
+ class="col-6"
|
|
|
emit-value
|
|
emit-value
|
|
|
map-options
|
|
map-options
|
|
|
:options="genderOptions"
|
|
:options="genderOptions"
|
|
@@ -57,61 +37,69 @@
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.email"
|
|
v-model="form.email"
|
|
|
label="E-mail"
|
|
label="E-mail"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
|
|
+ class="col-6"
|
|
|
|
|
+ type="email"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.phone"
|
|
v-model="form.phone"
|
|
|
label="Celular com DDD"
|
|
label="Celular com DDD"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
|
|
+ class="col-6"
|
|
|
:mask="masks.Brasil.celular"
|
|
:mask="masks.Brasil.celular"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultCepInput
|
|
<DefaultCepInput
|
|
|
v-model="form.cep"
|
|
v-model="form.cep"
|
|
|
- class="col-md-3 col-12"
|
|
|
|
|
|
|
+ class="col-4"
|
|
|
@rua="(v) => (form.address = v)"
|
|
@rua="(v) => (form.address = v)"
|
|
|
@bairro="(v) => (form.neighborhood = v)"
|
|
@bairro="(v) => (form.neighborhood = v)"
|
|
|
@uf="(v) => stateSelectRef?.selectStateByCode(v)"
|
|
@uf="(v) => stateSelectRef?.selectStateByCode(v)"
|
|
|
|
|
+ @cidade="(v) => citySelectRef?.selectCityByName(v)"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.address"
|
|
v-model="form.address"
|
|
|
label="Endereço"
|
|
label="Endereço"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
|
|
+ class="col-5"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.address_number"
|
|
v-model="form.address_number"
|
|
|
label="Número"
|
|
label="Número"
|
|
|
- class="col-md-3 col-12"
|
|
|
|
|
|
|
+ class="col-3"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.neighborhood"
|
|
v-model="form.neighborhood"
|
|
|
label="Bairro"
|
|
label="Bairro"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
|
|
+ class="col-4"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <CitySelect
|
|
|
|
|
+ ref="citySelectRef"
|
|
|
|
|
+ v-model="selectedCity"
|
|
|
|
|
+ label="Cidade"
|
|
|
|
|
+ class="col-4"
|
|
|
|
|
+ :state="selectedState"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<StateSelect
|
|
<StateSelect
|
|
|
ref="stateSelectRef"
|
|
ref="stateSelectRef"
|
|
|
- v-model="form.state"
|
|
|
|
|
|
|
+ v-model="selectedState"
|
|
|
label="Estado"
|
|
label="Estado"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
- outlined
|
|
|
|
|
- :initial-id="props.student.state_id ?? null"
|
|
|
|
|
|
|
+ class="col-4"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.complement"
|
|
v-model="form.complement"
|
|
|
label="Complemento"
|
|
label="Complemento"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
|
|
+ class="col-6"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultInput
|
|
<DefaultInput
|
|
|
v-model="form.payer"
|
|
v-model="form.payer"
|
|
|
label="Pagador"
|
|
label="Pagador"
|
|
|
- class="col-md-6 col-12"
|
|
|
|
|
|
|
+ class="col-6"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
<DefaultSelect
|
|
<DefaultSelect
|
|
@@ -128,25 +116,24 @@
|
|
|
label="Observações"
|
|
label="Observações"
|
|
|
class="col-12"
|
|
class="col-12"
|
|
|
type="textarea"
|
|
type="textarea"
|
|
|
|
|
+ :input-style="{ minHeight: '120px' }"
|
|
|
autogrow
|
|
autogrow
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="row justify-end q-mt-md" style="gap: 8px">
|
|
|
|
|
- <q-btn color="primary" label="Salvar" no-caps type="submit" :loading="loading" />
|
|
|
|
|
- </div>
|
|
|
|
|
</q-form>
|
|
</q-form>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, useTemplateRef } from "vue";
|
|
|
|
|
|
|
+import { ref, watch, onMounted, useTemplateRef } from "vue";
|
|
|
import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
|
import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
|
|
import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
|
|
|
import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
|
|
import DefaultInputDatePicker from "src/components/defaults/DefaultInputDatePicker.vue";
|
|
|
import DefaultCepInput from "src/components/defaults/DefaultCepInput.vue";
|
|
import DefaultCepInput from "src/components/defaults/DefaultCepInput.vue";
|
|
|
|
|
+import AvatarImageComponent from "src/components/shared/AvatarImageComponent.vue";
|
|
|
import StateSelect from "src/components/selects/StateSelect.vue";
|
|
import StateSelect from "src/components/selects/StateSelect.vue";
|
|
|
-import { useSubmitHandler } from "src/composables/useSubmitHandler";
|
|
|
|
|
-import { updateStudent } from "src/api/student";
|
|
|
|
|
|
|
+import CitySelect from "src/components/selects/CitySelect.vue";
|
|
|
|
|
+import { useInputRules } from "src/composables/useInputRules";
|
|
|
import masks from "src/helpers/masks";
|
|
import masks from "src/helpers/masks";
|
|
|
import { formatDateYMDtoDMY, formatDateDMYtoYMD } from "src/helpers/utils";
|
|
import { formatDateYMDtoDMY, formatDateDMYtoYMD } from "src/helpers/utils";
|
|
|
|
|
|
|
@@ -157,14 +144,16 @@ const props = defineProps({
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const emit = defineEmits(["saved"]);
|
|
|
|
|
|
|
+const { inputRules } = useInputRules();
|
|
|
|
|
|
|
|
const formRef = useTemplateRef("formRef");
|
|
const formRef = useTemplateRef("formRef");
|
|
|
-const fileInputRef = useTemplateRef("fileInputRef");
|
|
|
|
|
|
|
+const avatarRef = useTemplateRef("avatarRef");
|
|
|
const stateSelectRef = useTemplateRef("stateSelectRef");
|
|
const stateSelectRef = useTemplateRef("stateSelectRef");
|
|
|
|
|
+const citySelectRef = useTemplateRef("citySelectRef");
|
|
|
|
|
|
|
|
-const avatarPreview = ref(props.student.photo_url ?? null);
|
|
|
|
|
const avatarFile = ref(null);
|
|
const avatarFile = ref(null);
|
|
|
|
|
+const selectedState = ref(null);
|
|
|
|
|
+const selectedCity = ref(null);
|
|
|
|
|
|
|
|
const genderOptions = [
|
|
const genderOptions = [
|
|
|
{ label: "Prefiro não informar", value: "no_preference" },
|
|
{ label: "Prefiro não informar", value: "no_preference" },
|
|
@@ -182,7 +171,9 @@ const howFoundOptions = [
|
|
|
|
|
|
|
|
const form = ref({
|
|
const form = ref({
|
|
|
name: props.student.name ?? null,
|
|
name: props.student.name ?? null,
|
|
|
- birthdate: props.student.birth_date ? formatDateYMDtoDMY(props.student.birth_date) : null,
|
|
|
|
|
|
|
+ birthdate: props.student.birth_date
|
|
|
|
|
+ ? formatDateYMDtoDMY(props.student.birth_date)
|
|
|
|
|
+ : null,
|
|
|
cpf: props.student.document_number ?? null,
|
|
cpf: props.student.document_number ?? null,
|
|
|
gender: props.student.gender ?? "no_preference",
|
|
gender: props.student.gender ?? "no_preference",
|
|
|
email: props.student.email ?? null,
|
|
email: props.student.email ?? null,
|
|
@@ -191,28 +182,36 @@ const form = ref({
|
|
|
address: props.student.street ?? null,
|
|
address: props.student.street ?? null,
|
|
|
address_number: props.student.address_number ?? null,
|
|
address_number: props.student.address_number ?? null,
|
|
|
neighborhood: props.student.neighborhood ?? null,
|
|
neighborhood: props.student.neighborhood ?? null,
|
|
|
- state: null,
|
|
|
|
|
|
|
+ state_id: props.student.state_id ?? null,
|
|
|
|
|
+ city_id: props.student.city_id ?? null,
|
|
|
complement: props.student.complement ?? null,
|
|
complement: props.student.complement ?? null,
|
|
|
payer: props.student.payer_name ?? null,
|
|
payer: props.student.payer_name ?? null,
|
|
|
how_found: props.student.how_did_you_know_us ?? null,
|
|
how_found: props.student.how_did_you_know_us ?? null,
|
|
|
notes: props.student.notes ?? null,
|
|
notes: props.student.notes ?? null,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const { loading, execute } = useSubmitHandler({
|
|
|
|
|
- formRef,
|
|
|
|
|
- onSuccess: () => emit("saved"),
|
|
|
|
|
|
|
+watch(selectedState, (state) => {
|
|
|
|
|
+ form.value.state_id = state?.value ?? null;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-function triggerFileInput() {
|
|
|
|
|
- fileInputRef.value?.click();
|
|
|
|
|
-}
|
|
|
|
|
|
|
+watch(selectedCity, (city) => {
|
|
|
|
|
+ form.value.city_id = city?.value ?? null;
|
|
|
|
|
+});
|
|
|
|
|
|
|
|
-function onAvatarChange(event) {
|
|
|
|
|
- const file = event.target.files[0];
|
|
|
|
|
- if (file) {
|
|
|
|
|
- avatarFile.value = file;
|
|
|
|
|
- avatarPreview.value = URL.createObjectURL(file);
|
|
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ if (props.student.photo_url) {
|
|
|
|
|
+ avatarRef.value?.setImageUrl(props.student.photo_url);
|
|
|
}
|
|
}
|
|
|
|
|
+ if (props.student.state_id) {
|
|
|
|
|
+ stateSelectRef.value?.selectStateById(props.student.state_id);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (props.student.city_id) {
|
|
|
|
|
+ citySelectRef.value?.selectCityById(props.student.city_id);
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+function onAvatarChange(file) {
|
|
|
|
|
+ avatarFile.value = file;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function buildPayload() {
|
|
function buildPayload() {
|
|
@@ -230,8 +229,11 @@ function buildPayload() {
|
|
|
formData.append("street", form.value.address ?? "");
|
|
formData.append("street", form.value.address ?? "");
|
|
|
formData.append("address_number", form.value.address_number ?? "");
|
|
formData.append("address_number", form.value.address_number ?? "");
|
|
|
formData.append("neighborhood", form.value.neighborhood ?? "");
|
|
formData.append("neighborhood", form.value.neighborhood ?? "");
|
|
|
- if (form.value.state?.value) {
|
|
|
|
|
- formData.append("state_id", form.value.state.value);
|
|
|
|
|
|
|
+ if (form.value.state_id) {
|
|
|
|
|
+ formData.append("state_id", form.value.state_id);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (form.value.city_id) {
|
|
|
|
|
+ formData.append("city_id", form.value.city_id);
|
|
|
}
|
|
}
|
|
|
formData.append("complement", form.value.complement ?? "");
|
|
formData.append("complement", form.value.complement ?? "");
|
|
|
formData.append("payer_name", form.value.payer ?? "");
|
|
formData.append("payer_name", form.value.payer ?? "");
|
|
@@ -243,7 +245,9 @@ function buildPayload() {
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
name: form.value.name,
|
|
name: form.value.name,
|
|
|
- birth_date: form.value.birthdate ? formatDateDMYtoYMD(form.value.birthdate) : null,
|
|
|
|
|
|
|
+ birth_date: form.value.birthdate
|
|
|
|
|
+ ? formatDateDMYtoYMD(form.value.birthdate)
|
|
|
|
|
+ : null,
|
|
|
document_number: form.value.cpf,
|
|
document_number: form.value.cpf,
|
|
|
gender: form.value.gender,
|
|
gender: form.value.gender,
|
|
|
email: form.value.email || null,
|
|
email: form.value.email || null,
|
|
@@ -252,7 +256,8 @@ function buildPayload() {
|
|
|
street: form.value.address,
|
|
street: form.value.address,
|
|
|
address_number: form.value.address_number,
|
|
address_number: form.value.address_number,
|
|
|
neighborhood: form.value.neighborhood,
|
|
neighborhood: form.value.neighborhood,
|
|
|
- state_id: form.value.state?.value ?? null,
|
|
|
|
|
|
|
+ state_id: form.value.state_id,
|
|
|
|
|
+ city_id: form.value.city_id,
|
|
|
complement: form.value.complement,
|
|
complement: form.value.complement,
|
|
|
payer_name: form.value.payer,
|
|
payer_name: form.value.payer,
|
|
|
how_did_you_know_us: form.value.how_found,
|
|
how_did_you_know_us: form.value.how_found,
|
|
@@ -260,7 +265,8 @@ function buildPayload() {
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async function onSave() {
|
|
|
|
|
- await execute(() => updateStudent(buildPayload(), props.student.id));
|
|
|
|
|
-}
|
|
|
|
|
|
|
+defineExpose({
|
|
|
|
|
+ validate: () => formRef.value?.validate(),
|
|
|
|
|
+ buildPayload,
|
|
|
|
|
+});
|
|
|
</script>
|
|
</script>
|