LoginPage.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <template>
  2. <q-page class="login-page bg-surface-dark">
  3. <Transition name="fade-slide" mode="out-in">
  4. <div v-if="!clicked" key="splash" class="splash-screen" @click="clicked = true">
  5. <img :src="BackgroundLogin" class="splash-layer splash-layer--bg" />
  6. <img :src="FotoDiarista" class="splash-layer splash-layer--photo" />
  7. <img :src="LogoLogin" class="splash-layer splash-layer--logo" />
  8. </div>
  9. <div v-else-if="steps === 4" key="step4" class="splash-screen">
  10. <img :src="BackgroundLogin" class="splash-layer splash-layer--bg" />
  11. <img :src="FotoDiarista" class="splash-layer splash-layer--photo" />
  12. <img :src="LogoLogin" class="splash-layer splash-layer--logo-small" />
  13. <div class="step4-card-wrapper">
  14. <LoginStepFourPanel
  15. @back="steps = 3"
  16. @cep-resolved="onCepResolved"
  17. @device-location="onDeviceLocation"
  18. />
  19. </div>
  20. </div>
  21. <div v-else key="flow" class="flow-screen">
  22. <div class="flow-header">
  23. <q-btn
  24. v-if="steps === 3"
  25. flat
  26. dense
  27. color="primary"
  28. :label="$t('auth.register_later')"
  29. icon-right="mdi-chevron-right-circle-outline"
  30. class="text-caption"
  31. @click="steps = 4"
  32. />
  33. </div>
  34. <div class="flow-logo">
  35. <q-img :src="LogoDiariaCampos" style="max-width: 180px;" />
  36. </div>
  37. <q-form
  38. ref="loginForm"
  39. class="flow-form"
  40. autocorrect="off"
  41. autocapitalize="off"
  42. autocomplete="off"
  43. spellcheck="false"
  44. @submit="onSubmit"
  45. >
  46. <div class="flow-content" :class="{ 'flow-content--centered': steps < 3 }">
  47. <LoginStepOnePanel v-if="steps === 1" v-model:email="email" v-model:phone="phone" />
  48. <LoginStepTwoPanel v-else-if="steps === 2" v-model:code="code" />
  49. <LoginStepThreePanel v-else-if="steps === 3" v-model="stepThreeForm" />
  50. </div>
  51. <div class="flow-footer">
  52. <q-btn
  53. color="primary-button"
  54. :label="$t('auth.continue')"
  55. rounded
  56. padding="14px 16px"
  57. type="submit"
  58. class="full-width"
  59. :loading="submitting"
  60. >
  61. <template #loading>
  62. <q-spinner />
  63. </template>
  64. </q-btn>
  65. </div>
  66. </q-form>
  67. </div>
  68. </Transition>
  69. </q-page>
  70. </template>
  71. <script setup>
  72. import { ref } from 'vue';
  73. import { createUserAndClient, sendCode, validateCode } from 'src/api/user';
  74. import { useAuth } from 'src/composables/useAuth';
  75. import { useRouter } from 'vue-router';
  76. import { useRegistrationFlowStore } from 'src/stores/registrationFlow';
  77. import BackgroundLogin from 'src/assets/background-login.svg';
  78. import FotoDiarista from 'src/assets/foto_diarista_login.svg';
  79. import LogoLogin from 'src/assets/logo_diaria_login.svg';
  80. import LogoDiariaCampos from 'src/assets/logo_diaria_campos_login.svg';
  81. import LoginStepOnePanel from 'src/components/login/LoginStepOnePanel.vue';
  82. import LoginStepTwoPanel from 'src/components/login/LoginStepTwoPanel.vue';
  83. import LoginStepThreePanel from 'src/components/login/LoginStepThreePanel.vue';
  84. import LoginStepFourPanel from 'src/components/login/LoginStepFourPanel.vue';
  85. const router = useRouter();
  86. const { setAuthDataFromPayload } = useAuth();
  87. const flowStore = useRegistrationFlowStore();
  88. const email = ref('');
  89. const phone = ref('');
  90. const code = ref('');
  91. const stepThreeForm = ref({
  92. name: '',
  93. document: '',
  94. zip_code: '',
  95. address: '',
  96. complement: '',
  97. nickname: '',
  98. instructions: '',
  99. address_type: 'home',
  100. no_complement: false,
  101. city: '',
  102. state: '',
  103. });
  104. const steps = ref(1); // 1 = credentials | 2 = code | 3 = user fields | 4 = cep only
  105. const clicked = ref(false);
  106. const submitting = ref(false);
  107. const loginForm = ref(null);
  108. const isLogin = ref(false);
  109. const sendValidationCode = async () => {
  110. const response = await sendCode(email.value, phone.value);
  111. if (response.status === 201) {
  112. steps.value = 2;
  113. } else {
  114. console.error('Failed to send validation code');
  115. }
  116. return response;
  117. };
  118. const validateCodeInput = async () => {
  119. const response = await validateCode(email.value, phone.value, code.value, isLogin.value);
  120. if (response.status === 200) {
  121. if(isLogin.value == true) {
  122. await setAuthDataFromPayload(response.data.payload);
  123. router.push({ name: 'DashboardPage' });
  124. } else {
  125. steps.value = 3;
  126. }
  127. } else {
  128. console.error('Invalid validation code');
  129. }
  130. };
  131. const registerUserAndClient = async () => {
  132. const payload = {
  133. ...stepThreeForm.value,
  134. email: email.value,
  135. phone: phone.value,
  136. code: code.value,
  137. has_complement: !stepThreeForm.value.no_complement,
  138. };
  139. // delete payload.no_complement;
  140. const response = await createUserAndClient(payload);
  141. if (response.status === 200) {
  142. await setAuthDataFromPayload(response.data.payload);
  143. router.push({ name: 'DashboardPage' });
  144. } else {
  145. console.error('Failed to create user and client');
  146. }
  147. };
  148. const navigateToMap = (lat, lng) => {
  149. flowStore.setCredentials(email.value, phone.value, code.value);
  150. flowStore.setInitialLocation(lat, lng);
  151. router.push({ name: 'LocationMapPage' });
  152. };
  153. const onCepResolved = (geoData) => {
  154. navigateToMap(geoData.lat, geoData.lng);
  155. };
  156. const onDeviceLocation = ({ lat, lng }) => {
  157. navigateToMap(lat, lng);
  158. };
  159. const onSubmit = async () => {
  160. switch (steps.value) {
  161. case 1:
  162. {
  163. let response = await sendValidationCode();
  164. isLogin.value = response.data.payload.isLogin;
  165. break;
  166. }
  167. case 2: await validateCodeInput(); break;
  168. case 3: await registerUserAndClient(); break;
  169. default: break;
  170. }
  171. };
  172. </script>
  173. <style lang="scss" scoped>
  174. .fade-slide-enter-active,
  175. .fade-slide-leave-active {
  176. transition: opacity 0.35s ease, transform 0.35s ease;
  177. }
  178. .fade-slide-enter-from { opacity: 0; transform: translateY(6px); }
  179. .fade-slide-leave-to { opacity: 0; transform: translateY(-6px); }
  180. .login-page {
  181. min-height: 100vh;
  182. display: flex;
  183. justify-content: center;
  184. background: var(--q-surface-dark);
  185. }
  186. .splash-screen {
  187. position: relative;
  188. width: 100vw;
  189. min-height: 100vh;
  190. overflow: hidden;
  191. cursor: pointer;
  192. .splash-layer {
  193. position: absolute;
  194. &--bg {
  195. inset: 0;
  196. width: 100%;
  197. height: 100%;
  198. object-fit: cover;
  199. }
  200. &--photo {
  201. inset: 0;
  202. width: 100%;
  203. height: 100%;
  204. object-fit: cover;
  205. opacity: 0.15;
  206. mix-blend-mode: multiply;
  207. }
  208. &--logo {
  209. top: 50%;
  210. left: 50%;
  211. transform: translate(-50%, -50%);
  212. width: 180px;
  213. z-index: 1;
  214. }
  215. &--logo-small {
  216. top: 18%;
  217. left: 50%;
  218. transform: translate(-50%, -50%);
  219. width: 100px;
  220. z-index: 1;
  221. }
  222. }
  223. }
  224. .step4-card-wrapper {
  225. position: absolute;
  226. top: 50%;
  227. left: 50%;
  228. transform: translate(-50%, -30%);
  229. z-index: 2;
  230. width: 90%;
  231. max-width: 360px;
  232. }
  233. .flow-screen {
  234. display: flex;
  235. flex-direction: column;
  236. width: 100%;
  237. max-width: 500px;
  238. min-height: 100vh;
  239. padding: 16px 20px;
  240. background: var(--q-surface-dark);
  241. }
  242. .flow-header {
  243. min-height: 36px;
  244. display: flex;
  245. align-items: center;
  246. justify-content: flex-end;
  247. }
  248. .flow-logo {
  249. display: flex;
  250. justify-content: center;
  251. padding: 12px 0 24px;
  252. }
  253. .flow-form {
  254. flex: 1;
  255. display: flex;
  256. flex-direction: column;
  257. }
  258. .flow-content {
  259. flex: 1;
  260. &--centered {
  261. display: flex;
  262. align-items: center;
  263. justify-content: center;
  264. }
  265. }
  266. .flow-footer {
  267. padding: 20px 0 12px;
  268. }
  269. </style>