|
| 1 | +<template> |
| 2 | + <FormBlock :title="t.title" :description="t.description"> |
| 3 | + <template #content> |
| 4 | + <form id="contact-us" class="w-full hs-form" @submit.prevent="onSubmit"> |
| 5 | + <div class="flex flex-col gap-8"> |
| 6 | + <div class="flex flex-column gap-2 "> |
| 7 | + <label class="text-sm" for="name">{{ fields.name }}</label> |
| 8 | + <InputText type="text" id="name" v-model="name" v-bind="nameAttrs" :class="{ 'p-invalid': errors.name }" /> |
| 9 | + <small id="username-help" class="p-error" v-if="errors.first_name && meta.touched"> {{ t.errors['nameRequiredError'] }} </small> |
| 10 | + </div> |
| 11 | + <div class="flex flex-column gap-2 max-w-xl"> |
| 12 | + <label class="text-sm" for="email">{{ fields.email }}</label> |
| 13 | + <InputText type="text" v-model="email" id="email" class="w-full" v-bind="emailAttrs" :class="{ 'p-invalid': errors.email }" /> |
| 14 | + <small id="username-help" class="p-error" v-if="errors.email && meta.touched"> {{ t.errors['emailRequiredError'] }} </small> |
| 15 | + </div> |
| 16 | + <div class="flex flex-column gap-2 max-w-xl"> |
| 17 | + <label class="text-sm" for="phone">{{ fields.contactNumber }}</label> |
| 18 | + <InputText type="tel" v-model="phone" id="phone" class="w-full" v-bind="phoneAttrs" :class="{ 'p-invalid': errors.phone }" /> |
| 19 | + <small id="username-help" class="p-error" v-if="errors.phone && meta.touched"> {{ t.errors['phoneRequiredError'] }} </small> |
| 20 | + </div> |
| 21 | + <div class="flex flex-column gap-2 max-w-xl"> |
| 22 | + <label class="text-sm" for="resume">{{ fields.resume.label }}</label> |
| 23 | + <div class="flex gap-2 items-center" id="resume"> |
| 24 | + <FileUpload mode="basic" customUpload @uploader="uploadHandler" |
| 25 | + :invalidFileTypeMessage="fields.resume.invalidFileTypeMessage" |
| 26 | + accept="application/pdf,odt,txt,docx,doc,rtf" :chooseLabel="fields.resume.buttonText" |
| 27 | + :maxFileSize="5000000" auto /> |
| 28 | + <p v-if="fileName"> {{ fileName }} </p> |
| 29 | + </div> |
| 30 | + <small id="username-help" class="p-error" v-if="errors.resume && meta.touched"> {{ t.errors['resumeRequiredError'] }} </small> |
| 31 | + </div> |
| 32 | + <div class="flex flex-column gap-2 max-w-xl"> |
| 33 | + <label class="text-sm" for="linkedIn">{{ fields.linkedin }}</label> |
| 34 | + <InputText type="text" v-model="linkedinUsername" id="linkedIn" class="w-full" v-bind="linkedinUsernameAttrs" :class="{ 'p-invalid': errors.linkedin }" /> |
| 35 | + <small id="username-help" class="p-error" v-if="errors.linkedin && meta.touched"> {{ t.errors['linkedinRequiredError'] }} </small> |
| 36 | + </div> |
| 37 | + <div class="flex flex-column gap-2 max-w-xl"> |
| 38 | + <label class="text-sm" for="salary">{{ fields.deseriredSalaray }}</label> |
| 39 | + <InputText type="number" v-model="targetSalary" id="salary" class="w-full" v-bind="targetSalaryAttrs" :class="{ 'p-invalid': errors.salary }" /> |
| 40 | + <small id="username-help" class="p-error" v-if="errors.salary && meta.touched"> {{ t.errors['targetSalaryRequiredError'] }} </small> |
| 41 | + </div> |
| 42 | + </div> |
| 43 | + </form> |
| 44 | + <div class="mt-2" v-if="responseStatus !== 'default'"> |
| 45 | + <InlineMessage v-if="responseStatus === 'success'" severity="success" class="flex justify-start"> |
| 46 | + {{ feedbackMessages.success }} |
| 47 | + </InlineMessage> |
| 48 | + <InlineMessage v-if="responseStatus === 'error'" severity="error" class="flex justify-start"> |
| 49 | + {{ feedbackMessages.error }} |
| 50 | + </InlineMessage> |
| 51 | + </div> |
| 52 | + </template> |
| 53 | + <template #actions> |
| 54 | + <div class="w-full flex justify-end"> |
| 55 | + <Button :disabled="isSubmitting" class="w-fit" size="small" @click="onSubmit()"> {{ t.button.label }} </Button> |
| 56 | + </div> |
| 57 | + </template> |
| 58 | + </FormBlock> |
| 59 | +</template> |
| 60 | + |
| 61 | +<script setup> |
| 62 | +import InputText from "primevue/inputtext"; |
| 63 | +import Button from "primevue/button"; |
| 64 | +import InlineMessage from 'primevue/inlinemessage' |
| 65 | +import FileUpload from 'primevue/fileupload'; |
| 66 | +import { ref } from 'vue'; |
| 67 | +import { useForm } from 'vee-validate'; |
| 68 | +import * as yup from 'yup'; |
| 69 | +import { toTypedSchema } from '@vee-validate/yup'; |
| 70 | +import FormBlock from "../formblock/FormBlock.vue"; |
| 71 | +
|
| 72 | +const props = defineProps({ |
| 73 | + t: { |
| 74 | + type: Object, |
| 75 | + required: true |
| 76 | + }, |
| 77 | + jobId: { |
| 78 | + type: String, |
| 79 | + required: true |
| 80 | + } |
| 81 | +}) |
| 82 | +
|
| 83 | +const { t } = props |
| 84 | +const { fields, feedbackMessages } = t |
| 85 | +
|
| 86 | +const schema = toTypedSchema( |
| 87 | + yup.object({ |
| 88 | + name: yup.string().required('nameRequiredError'), |
| 89 | + email: yup.string().required('emailRequiredError').email('emailRequiredError'), |
| 90 | + phone: yup.string().required('phoneRequiredError'), |
| 91 | + targetSalary: yup.string().required('targetSalaryRequiredError'), |
| 92 | + linkedinUsername: yup.string().required('linkedinRequiredError'), |
| 93 | + file: yup.boolean().required('resumeRequiredError') |
| 94 | + }) |
| 95 | +); |
| 96 | +
|
| 97 | +const { defineField, handleSubmit, isSubmitting, meta, errors, setFieldValue } = useForm({ |
| 98 | + validationSchema: schema, |
| 99 | +}); |
| 100 | +
|
| 101 | +const [name, nameAttrs] = defineField('name'); |
| 102 | +const [email, emailAttrs] = defineField('email'); |
| 103 | +const [phone, phoneAttrs] = defineField('phone'); |
| 104 | +const [linkedinUsername, linkedinUsernameAttrs] = defineField('linkedinUsername'); |
| 105 | +const [targetSalary, targetSalaryAttrs] = defineField('targetSalary'); |
| 106 | +
|
| 107 | +const responseStatus = ref('default'); |
| 108 | +const fileName = ref(false); |
| 109 | +const formData = new FormData(); |
| 110 | +
|
| 111 | +const uploadHandler = async (event) => { |
| 112 | + const file = event.files[0]; |
| 113 | + fileName.value = file.name |
| 114 | + setFieldValue("file", true) |
| 115 | + formData.append("file", file) |
| 116 | +}; |
| 117 | +
|
| 118 | +function onSuccess(values) { |
| 119 | + handlePOST(values) |
| 120 | +} |
| 121 | +
|
| 122 | +const onSubmit = handleSubmit(onSuccess); |
| 123 | +
|
| 124 | +const handlePOST = async (values) => { |
| 125 | + const transformedValues = { |
| 126 | + ...values, |
| 127 | + fileName: fileName.value, |
| 128 | + targetSalary: { |
| 129 | + currency: "BRL", |
| 130 | + type: "CLT", |
| 131 | + value: Number(values.targetSalary) |
| 132 | + } |
| 133 | + }; |
| 134 | +
|
| 135 | + delete transformedValues["file"]; |
| 136 | +
|
| 137 | + const url = `https://www.azion.com/api/careers/post/inhire?id=${props.jobId}` |
| 138 | +
|
| 139 | + const request = { |
| 140 | + method: 'POST', |
| 141 | + body: formData, |
| 142 | + headers: { |
| 143 | + "X-Extra-Data": JSON.stringify(transformedValues) |
| 144 | + } |
| 145 | + }; |
| 146 | +
|
| 147 | + return fetch(url, request).then(function (response) { |
| 148 | + if (!response.ok) { |
| 149 | + throw new Error({ |
| 150 | + status: response.status, |
| 151 | + msg: '[!] Fetch ERRROR to API communication' |
| 152 | + }) |
| 153 | + } |
| 154 | + responseStatus.value = "success" |
| 155 | + return response.json(function (data) { |
| 156 | + return data; |
| 157 | + }); |
| 158 | + }).catch(function (error) { |
| 159 | + responseStatus.value = "error" |
| 160 | + throw new Error(error); |
| 161 | + }); |
| 162 | +} |
| 163 | +</script> |
0 commit comments