merge_generalImprovements #94

Merged
jsegarra merged 20 commits from merge_generalImprovements into beta 2024-11-27 14:50:49 +00:00
19 changed files with 15686 additions and 9359 deletions

View File

@ -6,13 +6,13 @@ Hedera is the main web page for Verdnatura.
Required dependencies. Required dependencies.
* PHP >= 7.0 - PHP >= 7.0
* Node.js >= 8.0 - Node.js >= 18.0
Launch application for development. Launch application for development.
``` ```
$ npm run dev $ quasar dev
``` ```
Launch project backend. Launch project backend.
@ -30,6 +30,6 @@ php hedera-web.php -m method_path
## Built with ## Built with
* [Webpack](https://webpack.js.org/) - [Webpack](https://webpack.js.org/)
* [MooTools](https://mootools.net/) - [MooTools](https://mootools.net/)
* [TinyMCE](https://www.tinymce.com/) - [TinyMCE](https://www.tinymce.com/)

View File

@ -12,9 +12,20 @@ module.exports = defineConfig({
numTestsKeptInMemory: 0, numTestsKeptInMemory: 0,
video: false, video: false,
screenshotOnRunFailure: false, screenshotOnRunFailure: false,
reporter: 'cypress-mochawesome-reporter',
reporterOptions: {
charts: true,
reportPageTitle: 'Hedera-Web E2E Reporter',
embeddedScreenshots: true,
reportDir: 'src/test/cypress/reports',
inlineAssets: true
},
setupNodeEvents(on, config) { setupNodeEvents(on, config) {
require('cypress-mochawesome-reporter/plugin')(on);
on('after:spec', (spec, results) => { on('after:spec', (spec, results) => {
console.log('Finished running', spec.relative); console.log('Finished running', spec.relative);
console.log('❌spec:', results.stats.failures);
console.log('✅spec:', results.stats.passes);
}); });
} }
} }

Binary file not shown.

View File

@ -5,6 +5,7 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"productName": "Verdnatura", "productName": "Verdnatura",
"author": "Verdnatura Levante SL", "author": "Verdnatura Levante SL",
"packageManager": "pnpm@8.15.1",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.verdnatura.es/hedera-web" "url": "https://git.verdnatura.es/hedera-web"
@ -15,39 +16,29 @@
"@babel/preset-env": "^7.20.2", "@babel/preset-env": "^7.20.2",
"@intlify/vue-i18n-loader": "^4.2.0", "@intlify/vue-i18n-loader": "^4.2.0",
"@quasar/app-webpack": "^3.0.0", "@quasar/app-webpack": "^3.0.0",
"@quasar/cli": "^2.4.1", "cypress-mochawesome-reporter": "^3.8.2",
"archiver": "^5.3.1", "eslint": "^8.57.0",
"assets-webpack-plugin": "^7.1.1",
"cypress": "^13.15.0",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0", "eslint-config-standard": "^17.0.0",
"eslint-plugin-cypress": "^2.13.3", "eslint-plugin-cypress": "^4.1.0",
"eslint-plugin-import": "^2.19.1", "eslint-plugin-import": "^2.19.1",
"eslint-plugin-n": "^15.0.0", "eslint-plugin-n": "^15.0.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^6.0.0", "eslint-plugin-promise": "^6.0.0",
"eslint-plugin-vue": "^9.27.0", "eslint-plugin-vue": "^9.27.0",
"eslint-webpack-plugin": "^4.2.0", "eslint-webpack-plugin": "^3.1.1",
"fs-extra": "^10.1.0", "file-loader": "^6.2.0",
"glob": "^11.0.0", "json-loader": "^0.5.7",
"mini-css-extract-plugin": "^2.7.0", "yaml-loader": "^0.5.0"
"postcss": "^8.4.39",
"postcss-import": "^13.0.0",
"postcss-loader": "^4.3.0",
"postcss-url": "^10.1.3"
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "^1.0.0", "@quasar/cli": "^2.3.0",
"@quasar/extras": "^1.16.9",
"axios": "^0.21.1", "axios": "^0.21.1",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"js-yaml": "^3.12.1",
"mootools": "^1.5.2",
"pinia": "^2.0.11", "pinia": "^2.0.11",
"promise-polyfill": "^8.2.3", "quasar": "^2.14.5",
"quasar": "^2.6.0", "require-yaml": "0.0.1",
"require-yaml": "^0.0.1",
"tinymce": "^6.3.0",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.0.0" "vue-router": "^4.0.0"

File diff suppressed because it is too large Load Diff

View File

@ -162,10 +162,7 @@ async function filterHandler(val, update) {
:rules="$attrs.required ? [requiredFieldRule] : null" :rules="$attrs.required ? [requiredFieldRule] : null"
virtual-scroll-slice-size="options.length" virtual-scroll-slice-size="options.length"
> >
<template <template v-if="isClearable" #append>
v-if="isClearable"
#append
>
<QIcon <QIcon
v-show="value" v-show="value"
name="close" name="close"
@ -179,11 +176,7 @@ async function filterHandler(val, update) {
#[slotName]="slotData" #[slotName]="slotData"
:key="slotName" :key="slotName"
> >
<slot <slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
:name="slotName"
v-bind="slotData ?? {}"
:key="slotName"
/>
</template> </template>
</QSelect> </QSelect>
</template> </template>

View File

@ -49,7 +49,7 @@ export default {
] ]
}, },
of: 'de', of: 'de',
startOrder: 'Començar comanda',
// Sections titles // Sections titles
titles: { titles: {
Home: 'Inici', Home: 'Inici',

View File

@ -2,16 +2,6 @@
// so you can safely delete all default props below // so you can safely delete all default props below
export default { export default {
failed: 'Action failed',
success: 'Action was successful',
internalServerError: 'Internal server error',
somethingWentWrong: 'Something went wrong',
loginFailed: 'Login failed',
authenticationRequired: 'Authentication required',
notFound: 'Not found',
today: 'Today',
yesterday: 'Yesterday',
tomorrow: 'Tomorrow',
language: 'Language', language: 'Language',
langs: { langs: {
en: 'English', en: 'English',
@ -61,7 +51,17 @@ export default {
'Dec' 'Dec'
] ]
}, },
startOrder: 'Start order',
failed: 'Action failed',
success: 'Action was successful',
internalServerError: 'Internal server error',
somethingWentWrong: 'Something went wrong',
loginFailed: 'Login failed',
authenticationRequired: 'Authentication required',
notFound: 'Not found',
today: 'Today',
yesterday: 'Yesterday',
tomorrow: 'Tomorrow',
// Sections titles // Sections titles
titles: { titles: {
Home: 'Home', Home: 'Home',

View File

@ -1,14 +1,4 @@
export default { export default {
failed: 'Acción fallida',
success: 'Acción exitosa',
internalServerError: 'Error interno del servidor',
somethingWentWrong: 'Algo salió mal',
loginFailed: 'Usuario o contraseña incorrectos',
authenticationRequired: 'Autenticación requerida',
notFound: 'No encontrado',
today: 'Hoy',
yesterday: 'Ayer',
tomorrow: 'Mañana',
language: 'Idioma', language: 'Idioma',
langs: { langs: {
en: 'Inglés', en: 'Inglés',
@ -58,6 +48,16 @@ export default {
'Dic' 'Dic'
] ]
}, },
failed: 'Acción fallida',
success: 'Acción exitosa',
internalServerError: 'Error interno del servidor',
somethingWentWrong: 'Algo salió mal',
loginFailed: 'Usuario o contraseña incorrectos',
authenticationRequired: 'Autenticación requerida',
notFound: 'No encontrado',
today: 'Hoy',
yesterday: 'Ayer',
tomorrow: 'Mañana',
// Sections titles // Sections titles
titles: { titles: {
@ -128,7 +128,6 @@ export default {
delete: 'Borrar', delete: 'Borrar',
confirmDelete: '¿Estás seguro de que quieres borrar la línea?', confirmDelete: '¿Estás seguro de que quieres borrar la línea?',
emptyList: 'Lista vacía', emptyList: 'Lista vacía',
orders: 'Pedidos', orders: 'Pedidos',
order: 'Pedido pendiente', order: 'Pedido pendiente',
ticket: 'Pedido', ticket: 'Pedido',
@ -149,6 +148,7 @@ export default {
remindMe: 'Recuérdame', remindMe: 'Recuérdame',
logInAsGuest: 'Entrar como invitado', logInAsGuest: 'Entrar como invitado',
logIn: 'Iniciar sesión', logIn: 'Iniciar sesión',
logOut: 'Cerrar sesión',
loginMail: "{'info'}{'@'}{'verdnatura.es'}", loginMail: "{'info'}{'@'}{'verdnatura.es'}",
loginPhone: '+34 963 242 100', loginPhone: '+34 963 242 100',
haveForgottenPassword: '¿Has olvidado tu contraseña?', haveForgottenPassword: '¿Has olvidado tu contraseña?',
@ -161,6 +161,7 @@ export default {
cancel: 'Cancelar', cancel: 'Cancelar',
of: 'de', of: 'de',
modify: 'Modificar', modify: 'Modificar',
startOrder: 'Empezar pedido',
shoppingCart: 'Cesta de la compra', shoppingCart: 'Cesta de la compra',
available: 'Disponible', available: 'Disponible',
minQuantity: 'Cantidad mínima', minQuantity: 'Cantidad mínima',

View File

@ -49,7 +49,7 @@ export default {
] ]
}, },
of: 'de', of: 'de',
startOrder: 'Lancer commande',
// Sections titles // Sections titles
titles: { titles: {
Home: 'Accueil', Home: 'Accueil',

View File

@ -49,6 +49,7 @@ export default {
] ]
}, },
of: 'de', of: 'de',
startOrder: 'Comece uma encomenda',
// Sections titles // Sections titles
titles: { titles: {
Home: 'Início', Home: 'Início',

View File

@ -47,7 +47,7 @@ const logoutSupplantedUser = async () => {
</script> </script>
<template> <template>
<QLayout view="lHh Lpr lFf"> <QLayout view="hhh Lpr fFf">
<QHeader> <QHeader>
<QToolbar> <QToolbar>
<QBtn <QBtn
@ -57,8 +57,10 @@ const logoutSupplantedUser = async () => {
icon="menu" icon="menu"
aria-label="Menu" aria-label="Menu"
@click="toggleLeftDrawer" @click="toggleLeftDrawer"
class="q-mr-md"
/> />
<QToolbarTitle> <img class="logo q-mr-lg" src="statics/logo-dark.svg" />
<QToolbarTitle data-testid="headerTitle">
{{ customTitle || menuTitle }} {{ customTitle || menuTitle }}
<div v-if="subtitle" class="subtitle text-caption"> <div v-if="subtitle" class="subtitle text-caption">
{{ subtitle }} {{ subtitle }}
@ -77,15 +79,13 @@ const logoutSupplantedUser = async () => {
</QBtn> </QBtn>
</QToolbar> </QToolbar>
</QHeader> </QHeader>
<QPageContainer :key="refreshContentKey">
<QDrawer <QDrawer
v-model="leftDrawerOpen" v-model="leftDrawerOpen"
:width="250" :width="250"
show-if-above show-if-above
data-testid="layoutMenuDrawer" data-testid="layoutMenuDrawer"
> >
<QToolbar class="logo">
<img src="statics/logo-dark.svg" />
</QToolbar>
<div class="user-info"> <div class="user-info">
<div> <div>
<span id="user-name" data-testid="layoutUserName"> <span id="user-name" data-testid="layoutUserName">
@ -97,7 +97,9 @@ const logoutSupplantedUser = async () => {
alt="_Exit" alt="_Exit"
@click="logout()" @click="logout()"
data-testid="logoutButton" data-testid="logoutButton"
/> >
<QTooltip>{{ $t('logOut') }}</QTooltip>
</QBtn>
</div> </div>
<div <div
v-if="supplantedUser" v-if="supplantedUser"
@ -139,7 +141,9 @@ const logoutSupplantedUser = async () => {
<QItemSection> <QItemSection>
<QItemLabel> <QItemLabel>
{{ {{
$t(`menuTitles.${subitem.description}`) $t(
`menuTitles.${subitem.description}`
)
}} }}
</QItemLabel> </QItemLabel>
</QItemSection> </QItemSection>
@ -148,7 +152,6 @@ const logoutSupplantedUser = async () => {
</QExpansionItem> </QExpansionItem>
</QList> </QList>
</QDrawer> </QDrawer>
<QPageContainer :key="refreshContentKey">
<router-view /> <router-view />
</QPageContainer> </QPageContainer>
</QLayout> </QLayout>
@ -158,14 +161,11 @@ const logoutSupplantedUser = async () => {
.q-toolbar { .q-toolbar {
min-height: 64px; min-height: 64px;
} }
.logo {
background-color: $primary;
justify-content: center;
& > img { .logo {
width: 160px; width: 160px;
}
} }
.user-info { .user-info {
margin: 25px; margin: 25px;

View File

@ -61,6 +61,10 @@ const updateConfigLang = async lang => {
try { try {
await vnFormRef.value.submit(); await vnFormRef.value.submit();
userStore.updateUserLang(lang); userStore.updateUserLang(lang);
const siteLocaleLang = appStore.localeOptions.find(
locale => locale.value === lang
).lang;
appStore.updateSiteLocale(siteLocaleLang);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
@ -124,6 +128,7 @@ onMounted(() => fetchLanguagesSql());
option-value="code" option-value="code"
:options="langOptions" :options="langOptions"
@update:model-value="updateConfigLang(data.lang)" @update:model-value="updateConfigLang(data.lang)"
data-testid="configViewLang"
/> />
</template> </template>
<template #extraForm> <template #extraForm>

View File

@ -20,8 +20,9 @@
icon="add_shopping_cart" icon="add_shopping_cart"
color="accent" color="accent"
to="/ecomerce/catalog" to="/ecomerce/catalog"
:title="$t('startOrder')" >
/> <QTooltip>{{ $t('startOrder') }}</QTooltip></QBtn
>
</QPageSticky> </QPageSticky>
</div> </div>
</template> </template>
@ -62,16 +63,3 @@ export default {
} }
}; };
</script> </script>
<i18n lang="yaml">
en-US:
startOrder: Start order
es-ES:
startOrder: Empezar pedido
ca-ES:
startOrder: Començar comanda
fr-FR:
startOrder: Lancer commande
pt-PT:
startOrder: Comece uma encomenda
</i18n>

View File

@ -20,16 +20,9 @@ onMounted(async () => await fetchData());
<template> <template>
<div style="padding: 0"> <div style="padding: 0">
<div class="q-pa-sm row items-start"> <div class="q-pa-sm row items-start">
<div <div class="new-card q-pa-sm" v-for="myNew in news" :key="myNew.id">
class="new-card q-pa-sm"
v-for="myNew in news"
:key="myNew.id"
>
<QCard> <QCard>
<VnImg <VnImg :id="myNew.image" storage="news" />
:id="myNew.image"
storage="news"
/>
<QCardSection> <QCardSection>
<div class="text-h5"> <div class="text-h5">
@ -37,10 +30,7 @@ onMounted(async () => await fetchData());
</div> </div>
</QCardSection> </QCardSection>
<QCardSection class="new-body"> <QCardSection class="new-body">
<div <div v-html="myNew.text" class="card-text" />
v-html="myNew.text"
class="card-text"
/>
</QCardSection> </QCardSection>
</QCard> </QCard>
</div> </div>
@ -51,14 +41,12 @@ onMounted(async () => await fetchData());
icon="add_shopping_cart" icon="add_shopping_cart"
color="accent" color="accent"
to="/ecomerce/catalog" to="/ecomerce/catalog"
:title="$t('startOrder')" >
/> <QTooltip>{{ $t('startOrder') }}</QTooltip></QBtn
>
</QPageSticky> </QPageSticky>
</div> </div>
<QDialog <QDialog v-model="showPreview" @hide="selectedImageSrc = ''">
v-model="showPreview"
@hide="selectedImageSrc = ''"
>
<QImg :src="selectedImageSrc" /> <QImg :src="selectedImageSrc" />
</QDialog> </QDialog>
</template> </template>

View File

@ -118,8 +118,9 @@ const onConfirmPay = async () => {
icon="add_shopping_cart" icon="add_shopping_cart"
color="accent" color="accent"
:to="{ name: 'catalog' }" :to="{ name: 'catalog' }"
:title="t('startOrder')" >
/> <QTooltip>{{ t('startOrder') }}</QTooltip></QBtn
>
</QPageSticky> </QPageSticky>
<VnConfirm <VnConfirm
v-model="showAmountToPayDialog" v-model="showAmountToPayDialog"

View File

@ -4,8 +4,16 @@ describe('Changes user nickname', () => {
cy.visit('/#/account/conf'); cy.visit('/#/account/conf');
}); });
it('success', () => { it('changes username', () => {
cy.changeUserNickname('Bruce Wayne', 'New test nickname'); cy.changeUserNickname('Bruce Wayne', 'New test nickname');
cy.resetDB(); cy.resetDB();
}); });
it('changes site lang when changing user lang', () => {
cy.dataCy('configViewLang').should('exist');
cy.selectOption('[data-testid="configViewLang"]', 'Español');
cy.dataCy('headerTitle').should('contain', 'Configuración');
cy.selectOption('[data-testid="configViewLang"]', 'English');
cy.dataCy('headerTitle').should('contain', 'Configuration');
});
}); });

View File

@ -1,83 +0,0 @@
#!/usr/bin/node
var glob = require('glob');
var fs = require('fs-extra');
var path = require('path');
var archiver = require('archiver');
let lang = process.argv[2];
if (lang == null)
{
let baseName = path.basename(process.argv[1]);
console.log(`Usage: ${baseName} language_code`);
process.exit(1);
}
let nTasks;
let projectDir;
(async () => {
await fs.remove(lang);
projectDir = await fs.realpath(`${__dirname}/..`);
projectDir = realPath;
let len = projectDir.length + 1;
glob(`${projectDir}/**/locale/`, async (err, localeDirs) => {
if (err) throw err;
for (let localeDir of localeDirs) {
localeDir = localeDir.substr(len)
if (/^node_modules\//.test (localeDir))
continue;
await exportLocale(localeDir);
}
nTasks = localeDirs.length;
});
})();
async function exportLocale (localeDir) {
let dstDir = `${lang}/${localeDir}`;
try {
await fs.mkdirp(dstDir);
let src, dst;
src = `${projectDir}/${localeDir}/en.json`;
dst = `${dstDir}/en.json`;
fs.copy(src, dst, onTaskEnd);
src = `${projectDir}/${localeDir}/${lang}.json`;
dst = `${dstDir}/${lang}.json`;
fs.copy(src, dst, onTaskEnd);
nTasks += 2;
}
catch (e) {
console.log(err);
}
nTasks--;
if (nTasks > 0) return;
let output = fs.createWriteStream(`${lang}.zip`);
output.on ('close', () =>{
fs.remove(lang);
console.log ('Export finalized!');
});
let archive = archiver('zip', {
zlib: { level: 9 }
});
archive.on ('error', err => {
throw err;
});
archive.pipe(output);
archive.directory(lang);
archive.finalize();
}

View File

@ -1,28 +0,0 @@
#!/usr/bin/node
var glob = require('glob');
var fs = require('fs-extra');
var yaml = require('js-yaml');
(async () => {
let projectDir = await fs.realpath(`${__dirname}/..`);
glob(`${projectDir}/**/locale/*.json`, async (err, localeFiles) => {
if (err) throw err;
for (let localeFile of localeFiles) {
if (/node_modules/.test (localeFile))
continue;
let dstFile = localeFile.replace(/\.json$/, '.yml');
let ymlString = yaml.safeDump(require(localeFile));
await fs.writeFile(dstFile, ymlString, 'utf8');
await fs.unlink(localeFile);
console.log('->', localeFile);
console.log(' ', dstFile);
}
console.log('Total %d files dumped.', localeFiles.length);
});
})();