|
|
@@ -1,11 +1,16 @@
|
|
|
<template>
|
|
|
<div>
|
|
|
- <DefaultHeaderPage title="Cadastro de Usuário" />
|
|
|
+ <DefaultHeaderPage
|
|
|
+ :title="isEdit ? 'Editar Usuário' : 'Cadastro de Usuário'"
|
|
|
+ />
|
|
|
|
|
|
<div class="q-pa-md">
|
|
|
<q-form ref="formRef">
|
|
|
<div class="column justify-center items-center q-mb-lg">
|
|
|
- <AvatarImageComponent @update:file="(f) => (avatarFile = f)" />
|
|
|
+ <AvatarImageComponent
|
|
|
+ ref="avatarRef"
|
|
|
+ @update:file="(f) => (avatarFile = f)"
|
|
|
+ />
|
|
|
|
|
|
<div class="row full-width q-mt-md q-col-gutter-sm">
|
|
|
<StateSelect
|
|
|
@@ -13,6 +18,7 @@
|
|
|
class="col-6"
|
|
|
outlined
|
|
|
label="Estado / UF"
|
|
|
+ :initial-id="editStateId"
|
|
|
/>
|
|
|
|
|
|
<UnitSelect
|
|
|
@@ -28,6 +34,7 @@
|
|
|
outlined
|
|
|
label="Tipo de Usuário"
|
|
|
:rules="[inputRules.required]"
|
|
|
+ :type="editUserTypeValue"
|
|
|
/>
|
|
|
|
|
|
<DefaultInput
|
|
|
@@ -61,21 +68,56 @@
|
|
|
label="Senha"
|
|
|
class="col-6"
|
|
|
outlined
|
|
|
- type="password"
|
|
|
- :rules="[inputRules.required, inputRules.min(8)]"
|
|
|
- />
|
|
|
+ :type="showPassword ? 'text' : 'password'"
|
|
|
+ :rules="
|
|
|
+ isEdit
|
|
|
+ ? [(v) => !v || v.length >= 8 || 'Mínimo 8 caracteres']
|
|
|
+ : [inputRules.required, inputRules.min(8)]
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <template #append>
|
|
|
+ <q-icon
|
|
|
+ :name="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
|
|
|
+ class="cursor-pointer"
|
|
|
+ color="secondary"
|
|
|
+ @click="showPassword = !showPassword"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </DefaultInput>
|
|
|
|
|
|
<DefaultInput
|
|
|
v-model="form.password_confirmation"
|
|
|
label="Repetir Senha"
|
|
|
class="col-6"
|
|
|
outlined
|
|
|
- type="password"
|
|
|
- :rules="[
|
|
|
- inputRules.required,
|
|
|
- inputRules.samePassword(form.password),
|
|
|
- ]"
|
|
|
- />
|
|
|
+ :type="showPasswordConfirm ? 'text' : 'password'"
|
|
|
+ :rules="
|
|
|
+ isEdit
|
|
|
+ ? [
|
|
|
+ (v) =>
|
|
|
+ !form.password ||
|
|
|
+ v === form.password ||
|
|
|
+ 'Senhas não conferem',
|
|
|
+ ]
|
|
|
+ : [
|
|
|
+ inputRules.required,
|
|
|
+ inputRules.samePassword(form.password),
|
|
|
+ ]
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <template #append>
|
|
|
+ <q-icon
|
|
|
+ :name="showPasswordConfirm ? 'mdi-eye-off' : 'mdi-eye'"
|
|
|
+ class="cursor-pointer"
|
|
|
+ color="secondary"
|
|
|
+ @click="showPasswordConfirm = !showPasswordConfirm"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </DefaultInput>
|
|
|
+
|
|
|
+ <div v-if="isEdit" class="col-12 text-caption text-grey-6">
|
|
|
+ Deixe os campos de senha em branco para manter a senha atual.
|
|
|
+ </div>
|
|
|
|
|
|
<div class="col-6 flex items-center">
|
|
|
<q-btn
|
|
|
@@ -103,8 +145,8 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref } from "vue";
|
|
|
-import { useRouter } from "vue-router";
|
|
|
+import { ref, computed, onMounted } from "vue";
|
|
|
+import { useRouter, useRoute } from "vue-router";
|
|
|
import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
|
|
|
import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
|
import AvatarImageComponent from "src/components/shared/AvatarImageComponent.vue";
|
|
|
@@ -113,14 +155,24 @@ import UnitSelect from "src/components/selects/UnitSelect.vue";
|
|
|
import UserTypeSelect from "src/components/selects/UserTypeSelect.vue";
|
|
|
import { useInputRules } from "src/composables/useInputRules";
|
|
|
import { useSubmitHandler } from "src/composables/useSubmitHandler";
|
|
|
-import { createUser } from "src/api/user";
|
|
|
+import { createUser, updateUser, getUserById } from "src/api/user";
|
|
|
|
|
|
const router = useRouter();
|
|
|
+const route = useRoute();
|
|
|
const { inputRules } = useInputRules();
|
|
|
|
|
|
const formRef = ref(null);
|
|
|
+const avatarRef = ref(null);
|
|
|
const avatarFile = ref(null);
|
|
|
|
|
|
+const showPassword = ref(false);
|
|
|
+const showPasswordConfirm = ref(false);
|
|
|
+
|
|
|
+const editUserTypeValue = ref(null);
|
|
|
+const editStateId = ref(null);
|
|
|
+
|
|
|
+const isEdit = computed(() => !!route.params.id);
|
|
|
+
|
|
|
const form = ref({
|
|
|
state: null,
|
|
|
unit: null,
|
|
|
@@ -139,6 +191,23 @@ const { loading, execute } = useSubmitHandler({
|
|
|
},
|
|
|
});
|
|
|
|
|
|
+onMounted(async () => {
|
|
|
+ if (!isEdit.value) return;
|
|
|
+ try {
|
|
|
+ const user = await getUserById(route.params.id);
|
|
|
+ form.value.name = user.name;
|
|
|
+ form.value.email = user.email;
|
|
|
+ form.value.cpf = user.cpf;
|
|
|
+ editUserTypeValue.value = user.user_type;
|
|
|
+ editStateId.value = user.state_id ?? null;
|
|
|
+ if (user.avatar_url) {
|
|
|
+ avatarRef.value?.setImageUrl(user.avatar_url);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("Failed to load user:", error);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
function buildFormData() {
|
|
|
const fd = new FormData();
|
|
|
|
|
|
@@ -156,8 +225,26 @@ function buildFormData() {
|
|
|
return fd;
|
|
|
}
|
|
|
|
|
|
+function buildUpdateData() {
|
|
|
+ const data = {};
|
|
|
+
|
|
|
+ if (form.value.name) data.name = form.value.name;
|
|
|
+ if (form.value.email) data.email = form.value.email;
|
|
|
+ if (form.value.cpf) data.cpf = form.value.cpf;
|
|
|
+ if (form.value.user_type?.value) data.user_type = form.value.user_type.value;
|
|
|
+ if (form.value.state?.value) data.state_id = form.value.state.value;
|
|
|
+ if (form.value.unit?.value) data.unit_id = form.value.unit.value;
|
|
|
+ if (form.value.password) data.password = form.value.password;
|
|
|
+
|
|
|
+ return data;
|
|
|
+}
|
|
|
+
|
|
|
async function onSave() {
|
|
|
- await execute(() => createUser(buildFormData()));
|
|
|
+ if (isEdit.value) {
|
|
|
+ await execute(() => updateUser(buildUpdateData(), route.params.id));
|
|
|
+ } else {
|
|
|
+ await execute(() => createUser(buildFormData()));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function generatePassword() {
|