#7750 - first-beta-review #95

Merged
jsegarra merged 26 commits from wbuezas/hedera-web-mindshore:first-beta-review into beta 2024-12-09 23:18:07 +00:00
27 changed files with 280 additions and 209 deletions

View File

@ -6,31 +6,30 @@ Hedera is the main web page for Verdnatura.
Required dependencies.
* PHP >= 7.0
* Node.js >= 18.0
- PHP >= 7.0
- Node.js >= 18.0
Launch application for development.
```
$ quasar dev
```
Launch Salix backend.
```
npm run salix
```
Launch legacy PHP backend.
```
npm run back
pnpm run back
```
Run server side method from command line.
```
php hedera-web.php -m method_path
```
## Built with
* [Webpack](https://webpack.js.org/)
* [MooTools](https://mootools.net/)
* [TinyMCE](https://www.tinymce.com/)
- [Webpack](https://webpack.js.org/)
- [MooTools](https://mootools.net/)
- [TinyMCE](https://www.tinymce.com/)

View File

@ -3,6 +3,7 @@ import { Connection } from '../js/db/connection';
import { useUserStore } from 'stores/user';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
import { useAppStore } from 'src/stores/app';
const { notify } = useNotify();
// Be careful when using SSR for cross-request state pollution
@ -37,9 +38,11 @@ const onResponseError = error => {
export default boot(({ app }) => {
const userStore = useUserStore();
const appStore = useAppStore();
function addToken(config) {
if (userStore.token) {
config.headers.Authorization = userStore.token;
config.headers['Accept-Language'] = appStore.siteLang;
}
return config;
}

View File

@ -129,17 +129,20 @@ async function filterHandler(val, update) {
if (!$props.defaultFilter) return update();
const newOptions = filter(val, myOptionsOriginal.value);
update(
() => {
myOptions.value = newOptions;
},
ref => {
if (val !== '' && ref.options.length > 0) {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
setTimeout(() => {
update(
() => {
myOptions.value = newOptions;
},
ref => {
if (val !== '' && ref.options.length > 0) {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
}
}
}
);
);
}, 300);
}
</script>
@ -178,6 +181,13 @@ async function filterHandler(val, update) {
>
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
</template>
<template #no-option>
<QItem>
<QItemSection class="text-grey">
{{ t('emptyList') }}
</QItemSection>
</QItem>
</template>
</QSelect>
</template>

View File

@ -100,26 +100,21 @@ const onSubmit = async () => {
en-US:
name: Name
file: File
send: Send
imageAdded: Image added successfully
es-ES:
name: Nombre
file: Archivo
send: Enviar
imageAdded: Imagen añadida correctamente
ca-ES:
name: Nom
file: Arxiu
send: Enviar
imageAdded: Imatge afegida correctament
fr-FR:
name: Nom
file: Fichier
send: Envoyer
imageAdded: Image ajoutée correctement
pt-PT:
name: Nome
file: Arquivo
send: Enviar
imageAdded: Imagen adicionada corretamente
</i18n>

View File

@ -67,6 +67,10 @@ const url = computed(() => {
return `${props.baseURL ?? app.imageUrl}/${props.storage}/${props.size}/${props.id}`;
});
const zoomUrl = computed(() => {
return `${props.baseURL ?? app.imageUrl}/${props.storage}/${props.zoomSize}/${props.id}`;
});
const rounded = computed(() => {
const roundedMap = {
none: '',
@ -114,7 +118,7 @@ const rounded = computed(() => {
<QDialog v-if="props.zoomSize" v-model="showZoom">
<QImg
:src="url"
:src="zoomUrl"
size="full"
class="img_zoom"
v-bind="$attrs"

View File

@ -6,10 +6,6 @@ defineProps({
type: String,
default: 'emptyList'
},
emptyIcon: {
type: String,
default: 'block'
},
rows: {
type: Array,
default: () => []
@ -29,7 +25,6 @@ const { t } = useI18n();
v-if="!rows?.length"
class="flex items-center q-pa-md justify-center items-center"
>
<QIcon :name="emptyIcon" size="sm" class="q-mr-sm" />
{{ t(emptyMessage) }}
</span>
<QSpinner v-if="loading" color="primary" size="3em" :thickness="2" />

View File

@ -40,6 +40,11 @@ const search = async () => {
query: searchTerm.value ? { search: searchTerm.value } : {}
});
if (!searchTerm.value) {
emit('onSearchError');
return;
}
if (props.sqlQuery) {
data = await jApi.query(props.sqlQuery, {
[props.searchField]: searchTerm.value

View File

@ -134,6 +134,7 @@ export default {
minQuantity: 'Quantitat mínima',
introduceSearchTerm: 'Introdueix un terme de cerca',
noOrdersFound: `No s'han trobat comandes`,
send: 'Enviar',
// Image related translations
'Cant lock cache': 'No es pot bloquejar la memòria cau',
'Bad file format': 'Format de fitxer no reconegut',

View File

@ -168,6 +168,7 @@ export default {
minQuantity: 'Minimum quantity',
introduceSearchTerm: 'Enter a search term',
noOrdersFound: 'No orders found',
send: 'Send',
// Image related translations
'Cant lock cache': 'The cache could not be blocked',
'Bad file format': 'Unrecognized file format',

View File

@ -167,6 +167,7 @@ export default {
minQuantity: 'Cantidad mínima',
introduceSearchTerm: 'Introduce un término de búsqueda',
noOrdersFound: 'No se encontrado pedidos',
send: 'Enviar',
// Image related translations
'Cant lock cache': 'La caché no pudo ser bloqueada',
'Bad file format': 'Formato de archivo no reconocido',

View File

@ -135,6 +135,7 @@ export default {
minQuantity: 'Quantité minimum',
introduceSearchTerm: 'Entrez un terme de recherche',
noOrdersFound: 'Aucune commande trouvée',
send: 'Envoyer',
// Image related translations
'Cant lock cache': "Le cache n'a pas pu être verrouillé",
'Bad file format': 'Format de fichier non reconnu',

View File

@ -133,6 +133,7 @@ export default {
minQuantity: 'Quantidade mínima',
introduceSearchTerm: 'Digite um termo de pesquisa',
noOrdersFound: 'Nenhum pedido encontrado',
send: 'Enviar',
// Image related translations
'Cant lock cache': 'O cache não pôde ser bloqueado',
'Bad file format': 'Formato de arquivo inválido',

View File

@ -3,13 +3,11 @@
id="bg"
class="fullscreen row justify-center items-center layout-view scroll"
>
<div class="column q-pa-md row items-center justify-center">
<router-view v-slot="{ Component }">
<transition>
<component :is="Component" />
</transition>
</router-view>
</div>
<QPageContainer class="column q-pa-md row items-center justify-center">
<transition>
<router-view />
</transition>
</QPageContainer>
</QLayout>
</template>

View File

@ -47,7 +47,7 @@ const logoutSupplantedUser = async () => {
</script>
<template>
<QLayout view="hhh Lpr fFf">
<QLayout view="hhh LpR fFf">
<QHeader>
<QToolbar>
<QBtn
@ -216,6 +216,10 @@ const logoutSupplantedUser = async () => {
padding: 16px;
}
div .q-drawer-container {
padding: 0 !important;
}
@include mobile {
#actions {
.q-btn {

View File

@ -109,12 +109,12 @@ const fetchData = async () => {
</QBtn>
<QBtn
icon="shopping_bag"
:label="t('catalog')"
:label="t('titles.Catalog')"
:to="{ name: 'catalog' }"
rounded
no-caps
>
<QTooltip>{{ t('catalog') }}</QTooltip>
<QTooltip>{{ t('titles.Catalog') }}</QTooltip>
</QBtn>
<QBtn
icon="shopping_cart_checkout"

View File

@ -25,8 +25,9 @@ const { t } = useI18n();
storage="catalog"
size="200x200"
:id="item.image"
height="210px"
height="190px"
rounded="bottom"
zoom-size="1600x900"
/>
<div
class="column"
@ -45,7 +46,7 @@ const { t } = useI18n();
</span>
<span> #{{ item.id }}</span>
</div>
<div class="tags q-pt-xs text-caption">
<div class="tags text-caption">
<div
v-for="(tag, index) in item.previewTags"
:key="index"
@ -75,7 +76,7 @@ const { t } = useI18n();
<div class="row justify-between items-cente q-gutter-x-xs">
<QBadge
:label="`x${item.grouping}`"
color="grey"
color="grey-4"
class="col-2 justify-end text-body2"
>
<QTooltip>
@ -85,8 +86,7 @@ const { t } = useI18n();
<QBadge
outline
:label="item.available"
color="accent"
text-color="black"
color="grey-6"
class="col justify-end text-body2"
>
<QTooltip>
@ -97,7 +97,6 @@ const { t } = useI18n();
outline
:label="currency(item.price)"
color="accent"
text-color="black"
class="col justify-end text-body2"
>
<QTooltip>
@ -117,6 +116,7 @@ const { t } = useI18n();
height="105px"
rounded-borders="full"
class="q-mr-md"
zoom-size="1600x900"
/>
</template>
<template #content>
@ -153,7 +153,8 @@ const { t } = useI18n();
<div class="row justify-end items-center q-gutter-x-xs q-mt-sm">
<QBadge
:label="`x${item.grouping}`"
color="grey"
color="grey-4"
text-color="black"
class="col-2 justify-end text-body2"
>
<QTooltip>
@ -163,8 +164,7 @@ const { t } = useI18n();
<QBadge
outline
:label="item.available"
color="accent"
text-color="black"
color="grey-6"
class="col-3 justify-end text-body2"
>
<QTooltip>
@ -175,7 +175,6 @@ const { t } = useI18n();
outline
:label="currency(item.price)"
color="accent"
text-color="black"
class="col-3 justify-end text-body2"
>
<QTooltip>

View File

@ -1,7 +1,14 @@
<template>
<Teleport v-if="isHeaderMounted" to="#actions">
<div class="row">
<VnSearchBar :search-term="search" @on-search-error="items = []" />
<VnSearchBar
@on-search-error="
() => {
items = [];
search = '';
}
"
/>
<QBtn
:icon="viewTypeButtonContent.icon"
:label="viewTypeButtonContent.label"
@ -36,7 +43,7 @@
/>
</div>
</Teleport>
<div style="padding-bottom: 5em">
<div>
<QDrawer v-model="rightDrawerOpen" side="right" :width="250" persistent>
<div class="q-pa-md">
<div class="basket-info q-gutter-y-sm">
@ -55,6 +62,9 @@
no-caps
@click="redirectToCheckout()"
data-testid="orderModifyButton"
color="light-green-7"
unelevated
text-color="white"
>
{{ t('modify') }}
</QBtn>
@ -132,6 +142,12 @@
:disable="!category"
:label="t('category')"
/>
<div
v-if="isSomeFilterSelected"
class="q-mt-md text-grey-7"
>
{{ t('orderBy') }}
</div>
<VnSelect
v-if="isSomeFilterSelected"
v-model="selectedOrderBy"
@ -139,7 +155,7 @@
option-value="value"
option-label="label"
:is-clearable="false"
:label="t('orderBy')"
:label="t('sort')"
/>
</div>
<span
@ -153,7 +169,7 @@
<div
:class="
viewMode === 'grid'
? 'q-pa-md row justify-center q-gutter-md'
? ' row justify-center q-gutter-md'
: 'column items-center'
"
>
@ -167,7 +183,6 @@
v-else-if="!items || !items.length || !isSomeFilterSelected"
class="text-subtitle1 text-grey-7 q-pa-md"
>
<QIcon name="refresh" size="sm" class="q-mr-sm"></QIcon>
<span>{{ t('pleaseSetFilter') }}</span>
</div>
<CatalogCard
@ -180,13 +195,13 @@
/>
</div>
<QDialog v-model="showItemDialog" @hide="resetAmounts()">
<QCard style="width: 25em" class="column">
<QCard v-if="selectedItem" style="width: 25em" class="column">
<div class="q-pa-md relative-position">
<div class="q-mb-md" style="display: flex">
<VnImg
storage="catalog"
size="200x200"
:id="'asd'"
:id="selectedItem.image"
width="112px"
height="112px"
rounded="bottom"
@ -282,6 +297,7 @@
</QBtn>
</div>
</QCard>
<QSpinner v-else color="primary" size="3em" :thickness="5" />
</QDialog>
</div>
</template>
@ -744,7 +760,8 @@ const getSubcategories = async () => {
DROP TEMPORARY TABLE tmp.itemAvailable;`,
{ orderId: basketOrderId.value }
);
itemSubcategories.value = res.results[1].data;
const filtered = res.results[1].data.filter(item => item.category);
itemSubcategories.value = filtered.map(i => i.category);
} catch (error) {
console.error('Error getting subcategories:', error);
Review

En lilium hemos quitado los trycatch porque si la peticion falla, el usuario no se entera.
Ya tenemos el controlador de axios, no?

En lilium hemos quitado los trycatch porque si la peticion falla, el usuario no se entera. Ya tenemos el controlador de axios, no?
Review

Si, tenemos el controlador de axios

Si, tenemos el controlador de axios
}
@ -753,11 +770,13 @@ const getSubcategories = async () => {
const showItem = async item => {
if (checkGuest()) return;
const itemLots = await calcItem(item.id);
const tags = await getItemTags(item.id);
showItemDialog.value = true;
const [itemLots, tags] = await Promise.all([
calcItem(item.id),
getItemTags(item.id)
]);
item.lots = itemLots;
item.tags = tags;
showItemDialog.value = true;
selectedItem.value = item;
};
@ -848,6 +867,7 @@ const onAddLotClick = async lot => {
};
const resetAmounts = () => {
selectedItem.value = null;
addedItemsAmountAcc.value = {};
amount.value = 0;
};
@ -1031,6 +1051,8 @@ en-US:
filterBy: Filter by
chooseCategory: Choose a category
youMustBeLoggedIn: You must be a registered user
sort: Order
amountNotAvailable: Amount not available
es-ES:
category: Categoría
deleteFilter: Quitar filtro
@ -1054,6 +1076,8 @@ es-ES:
filterBy: Filtrar por
chooseCategory: Elige una categoría
youMustBeLoggedIn: Debes estar registrado como usuario
sort: Ordenar
amountNotAvailable: Cantidad no disponible
ca-ES:
category: Categoría
deleteFilter: Eliminar filtro
@ -1075,6 +1099,8 @@ ca-ES:
filterBy: Filtrar per
chooseCategory: Tria una categoria
youMustBeLoggedIn: Has d'estar registrat com a usuari
sort: Ordenar
amountNotAvailable: Quantitat no disponible
fr-FR:
category: Catégorie
deleteFilter: Supprimer le filtre
@ -1096,6 +1122,8 @@ fr-FR:
filterBy: Filtrer par
chooseCategory: Choisissez une catégorie
youMustBeLoggedIn: Vous devez être un utilisateur enregistré
sort: Trier
amountNotAvailable: Quantité non disponible
pt-PT:
category: Categoria
deleteFilter: Apagar filtro
@ -1117,4 +1145,6 @@ pt-PT:
filterBy: Filtrar por
chooseCategory: Escolha uma categoria
youMustBeLoggedIn: Deves estar registrado como usuario
sort: Ordenar
amountNotAvailable: Quantidade não disponível
</i18n>

View File

@ -1,5 +1,5 @@
<script setup>
import { ref, onMounted, inject, computed } from 'vue';
import { ref, onMounted, inject, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
@ -28,6 +28,7 @@ const agencies = ref([]);
const warehouses = ref([]);
const currentStep = ref('method');
const id = route.params.id;
const defaultValues = ref(null);
const orderForm = ref({
method: 'AGENCY',
date: formatDate(Date.vnNew(), 'YYYY/MM/DD'),
@ -179,6 +180,15 @@ const getAgencies = async () => {
}
);
agencies.value = results[1].data;
if (agencies.value && agencies.value.length && defaultValues.value) {
const found = agencies.value.find(
agency => agency.id === defaultValues.value.defaultAgencyFk
);
Review

Duda, si no se encuentra no debería mostrar mensaje de error al usuario?
Cambiar found por agency

Duda, si no se encuentra no debería mostrar mensaje de error al usuario? Cambiar found por agency
Review

No creo que se deba mostrar un error por esto, supongo que por algo el hedera viejo no lo hacía

No creo que se deba mostrar un error por esto, supongo que por algo el hedera viejo no lo hacía
if (found)
orderForm.value.agency = defaultValues.value.defaultAgencyFk;
}
} catch (error) {
console.error('Error getting agencies:', error);
}
@ -284,6 +294,13 @@ const submit = async () => {
}
};
const getDefaultValues = async () => {
return await jApi.query(
`SELECT deliveryMethod, agencyModeFk, addressFk, defaultAgencyFk
FROM myBasketDefaults`
);
};
onMounted(async () => {
today.value = Date.vnNew();
today.value.setHours(0, 0, 0, 0);
@ -305,10 +322,21 @@ onMounted(async () => {
orderForm.value.agency = order.agencyModeFk;
orderForm.value.address = order.addressFk;
}
} else {
const [_defaultValues] = await getDefaultValues();
if (_defaultValues) defaultValues.value = _defaultValues;
}
getAddresses();
});
watch(
() => orderForm.value.method,
() => {
orderForm.value.address = '';
orderForm.value.agency = '';
}
);
</script>
<template>
@ -468,7 +496,7 @@ onMounted(async () => {
</QTooltip>
</QBtn>
<QBtn
v-if="showNavigationButtons"
v-if="showNavigationButtons || currentStep === 'confirm'"
@click="onNextStep(stepIndex)"
:color="currentStep === 'confirm' ? 'accent ' : 'primary'"
:icon="
@ -477,6 +505,7 @@ onMounted(async () => {
dense
class="right-navigation-button"
data-testid="checkoutStepperRightButton"
:loading="loading"
>
<QTooltip>
{{ t(`${step.nextButtonLabel || 'next'}`) }}
@ -491,7 +520,7 @@ onMounted(async () => {
@import 'src/css/responsive';
.step-title {
min-width: 100%;
max-width: 90%;
margin-bottom: 16px;
text-align: center;
font-weight: bold;
@ -523,7 +552,7 @@ onMounted(async () => {
.left-navigation-button {
position: absolute;
left: 5px;
top: 50%;
top: 25px;
@include mobile {
top: 35%;
}
@ -532,7 +561,7 @@ onMounted(async () => {
.right-navigation-button {
position: absolute;
right: 5px;
top: 50%;
top: 25px;
@include mobile {
top: 35%;
}

View File

@ -288,9 +288,8 @@ onMounted(async () => {
>
<div
v-if="!transferAccounts.length"
class="row items-center justify-center q-pa-md bg-red"
class="row items-center justify-center q-pa-md"
>
<QIcon class="q-mr-md" name="block" size="sm" />
<span>{{ t('emptyList') }}</span>
</div>
<QList>

View File

@ -172,7 +172,6 @@ const deleteRow = id => {
class="row items-center justify-center q-pa-md"
style="margin-top: 32px"
>
<QIcon class="q-mr-md" name="block" size="sm" />
<span>{{ t('emptyList') }}</span>
</div>
</QCard>

View File

@ -136,8 +136,11 @@ const loginAsGuest = async () => {
outline
/>
</div>
<p class="password-forgotten text-center q-mt-lg">
<router-link to="/remember-password" class="link">
<p
class="password-forgotten text-center q-mt-lg"
data-testid="recoverPasswordViewLink"
>
<router-link :to="{ name: 'recoverPassword' }" class="link">
{{ $t('haveForgottenPassword') }}
</router-link>
</p>

View File

@ -0,0 +1,99 @@
<script setup>
import { ref } from 'vue';
import { api } from 'boot/axios';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import useNotify from 'src/composables/useNotify.js';
import VnInput from 'src/components/common/VnInput.vue';
const user = ref('');
const router = useRouter();
const { t } = useI18n();
const { notify } = useNotify();
const onSend = async () => {
const params = {
user: user.value,
app: 'hedera'
};
await api.post('VnUsers/recoverPassword', params);
notify(t('weHaveSentEmailToRecover'), 'positive');
router.push('/login');
};
</script>
<template>
<QPage class="text-center">
<div>
<QIcon
name="contact_support"
class="block q-mx-auto text-accent"
style="font-size: 120px"
/>
</div>
<div>
<QForm @submit="onSend" class="q-gutter-y-md text-grey-8">
<VnInput
v-model="user"
:label="t('user')"
autofocus
data-testid="recoverPasswordUserInput"
/>
<div class="q-mt-lg">
{{ t('weSendEmail') }}
</div>
<div>
<QBtn
type="submit"
:label="t('send')"
class="full-width q-mt-md"
color="primary"
rounded
no-caps
unelevated
data-testid="recoverPasswordSubmitButton"
/>
<div class="text-center q-mt-md">
<router-link to="/login" class="link">
{{ t('back') }}
</router-link>
</div>
</div>
</QForm>
</div>
</QPage>
</template>
<style lang="scss" scoped>
.q-btn {
height: 50px;
}
</style>
<i18n lang="yaml">
en-US:
inputEmail: Input email
rememberPassword: Rememeber password
weSendEmail: We will sent you an email to recover your password
weHaveSentEmailToRecover: We've sent you an email where you can recover your password
es-ES:
inputEmail: Introduce el correo electrónico
rememberPassword: Recordar contraseña
weSendEmail: Te enviaremos un correo para restablecer tu contraseña
weHaveSentEmailToRecover: Te hemos enviado un correo donde podrás recuperar tu contraseña
ca-ES:
inputEmail: Introdueix el correu electrònic
rememberPassword: Recordar contrasenya
weSendEmail: T'enviarem un correu per restablir la teva contrasenya
weHaveSentEmailToRecover: T'hem enviat un correu on podràs recuperar la teva contrasenya
fr-FR:
inputEmail: Entrez l'email
rememberPassword: Se souvenir du mot de passe
weSendEmail: Nous vous enverrons un e-mail pour récupérer votre mot de passe
weHaveSentEmailToRecover: Nous vous avons envoyé un e-mail vous pouvez récupérer votre mot de passe
pr-BR:
inputEmail: Digite o e-mail
rememberPassword: Lembrar senha
weSendEmail: Enviaremos um e-mail para recuperar sua senha
weHaveSentEmailToRecover: Enviamos um e-mail onde você pode recuperar sua senha
</i18n>

View File

@ -1,114 +0,0 @@
<template>
<div class="text-center">
<div>
<QIcon
name="contact_support"
class="block q-mx-auto text-accent"
style="font-size: 120px"
/>
</div>
<div>
<QForm
@submit="onSend"
class="q-gutter-y-md text-grey-8"
>
<div class="text-h5">
<div>
{{ $t('dontWorry') }}
</div>
<div>
{{ $t('fillData') }}
</div>
</div>
<QInput
v-model="email"
:label="$t('user')"
:rules="[val => !!val || $t('inputEmail')]"
autofocus
/>
<div class="q-mt-lg">
{{ $t('weSendEmail') }}
</div>
<div>
<QBtn
type="submit"
:label="$t('send')"
class="full-width q-mt-md"
color="primary"
rounded
no-caps
unelevated
/>
<div class="text-center q-mt-md">
<router-link
to="/login"
class="link"
>
{{ $t('return') }}
</router-link>
</div>
</div>
</QForm>
</div>
</div>
</template>
<style lang="scss" scoped>
#image {
height: 190px;
}
.q-btn {
height: 50px;
}
a {
color: inherit;
font-size: 0.8rem;
}
</style>
<script>
export default {
name: 'VnRememberPasword',
data() {
return {
email: ''
};
},
methods: {
async onSend() {
const params = {
email: this.email
};
await this.$axios.post('Users/reset', params);
this.$q.notify({
message: this.$t('weHaveSentEmailToRecover'),
type: 'positive'
});
this.$router.push('/login');
}
}
};
</script>
<i18n lang="yaml">
en-US:
user: User
inputEmail: Input email
rememberPassword: Rememeber password
dontWorry: Don't worry!
fillData: Fill the data
weSendEmail: We will sent you an email to recover your password
weHaveSentEmailToRecover: We've sent you an email where you can recover your password
send: Send
return: Return
es-ES:
user: Usuario
inputEmail: Introduce el correo electrónico
rememberPassword: Recordar contraseña
dontWorry: ¡No te preocupes!
fillData: Rellena los datos
weSendEmail: Te enviaremos un correo para restablecer tu contraseña
weHaveSentEmailToRecover: Te hemos enviado un correo donde podrás recuperar tu contraseña
send: Enviar
return: Volver
</i18n>

View File

@ -39,10 +39,11 @@ export default route(function (/* { store, ssrContext } */) {
Router.beforeEach((to, from, next) => {
const userStore = useUserStore();
const allowedRoutes = ['login', 'recoverPassword'];
if (
!userStore.storage.getItem('token') &&
to.name !== 'login' &&
!allowedRoutes.includes(to.name) &&
!userStore.isGuest
) {
return next({ name: 'login' });

View File

@ -5,17 +5,17 @@ const routes = [
children: [
{
name: 'login',
path: '/login/:email?',
path: '',
component: () => import('pages/Login/LoginView.vue')
},
{
name: 'rememberPassword',
path: '/remember-password',
component: () => import('pages/Login/RememberPassword.vue')
name: 'recoverPassword',
path: 'recover',
component: () => import('pages/Login/RecoverPassword.vue')
},
{
name: 'resetPassword',
path: '/reset-password',
path: 'reset',
component: () => import('pages/Login/ResetPassword.vue')
}
]

View File

@ -20,12 +20,7 @@ export const useAppStore = defineStore('hedera', {
menuEssentialLinks: [],
hiddenMenuLinks: new Set(['Reports']),
basketOrderId: null,
localeDates: {
days: [],
months: [],
daysShort: [],
monthsShort: []
},
siteLang: null,
localeOptions: [
{ label: t('langs.en'), lang: 'en-US', value: 'en' },
@ -66,20 +61,9 @@ export const useAppStore = defineStore('hedera', {
this.$patch({ imageUrl });
},
getLocaleDates() {
const { messages, locale } = i18n.global;
this.localeDates = {
days: messages.value[locale.value].date.days,
months: messages.value[locale.value].date.months,
daysShort: messages.value[locale.value].date.daysShort,
monthsShort: messages.value[locale.value].date.monthsShort
};
},
async init() {
this.updateSiteLocale(localStorage.getItem('siteLang') || 'es-ES');
this.getBasketOrderId();
this.getLocaleDates();
},
getBasketOrderId() {
@ -187,6 +171,12 @@ export const useAppStore = defineStore('hedera', {
isDesktop() {
const $q = useQuasar();
return $q?.screen?.width > 1024;
},
localeDates() {
const { messages, locale } = i18n.global;
const { days, months, daysShort, monthsShort } =
messages.value[locale.value].date;
return { days, months, daysShort, monthsShort };
}
}
});

View File

@ -0,0 +1,18 @@
describe('Login Tests', () => {
beforeEach(() => {
cy.visit('/#/login');
});
it('should ssend recover email', () => {
cy.dataCy('recoverPasswordViewLink').should('exist');
cy.dataCy('recoverPasswordViewLink').click();
cy.dataCy('recoverPasswordUserInput').find('input').should('exist');
cy.dataCy('recoverPasswordUserInput').find('input').type('developer');
cy.dataCy('recoverPasswordSubmitButton').should('exist');
cy.dataCy('recoverPasswordSubmitButton').click();
cy.checkNotify(
'positive',
'Te hemos enviado un correo donde podrás recuperar tu contraseña'
);
});
});