diff --git a/src/components/ui/CardList.vue b/src/components/ui/CardList.vue
index b8d5c6c6..cd3b0767 100644
--- a/src/components/ui/CardList.vue
+++ b/src/components/ui/CardList.vue
@@ -1,7 +1,7 @@
@@ -90,8 +100,8 @@ onMounted(() => fetchLanguagesSql());
+import { onMounted, inject, ref } from 'vue';
+import { useRoute } from 'vue-router';
+
+import CardList from 'src/components/ui/CardList.vue';
+
+import { formatDateTitle } from 'src/lib/filters.js';
+
+const jApi = inject('jApi');
+
+const route = useRoute();
+
+const accessLogs = ref([]);
+const user = ref(null);
+
+const getUser = async () => {
+ try {
+ if (!route.params.id) return;
+ const [data] = await jApi.query(
+ `SELECT u.id, u.name user, u.nickname, u.email, c.phone, r.name role
+ FROM account.user u
+ JOIN account.role r ON r.id = u.role
+ LEFT JOIN vn.client c ON c.id = u.id
+ WHERE u.id = #user`,
+ { user: route.params.id }
+ );
+ user.value = data;
+ } catch (error) {
+ console.error('Error getting user:', error);
+ }
+};
+
+const getAccessLogs = async () => {
+ try {
+ accessLogs.value = await jApi.query(
+ `SELECT u.stamp, a.platform, a.browser, a.version, a.javascript, a.cookies
+ FROM visitUser u
+ JOIN visitAccess c ON c.id = u.accessFk
+ JOIN visitAgent a ON a.id = c.agentFk
+ WHERE u.userFk = #user
+ ORDER BY u.stamp DESC
+ LIMIT 8`,
+ { user: route.params.id }
+ );
+ } catch (error) {
+ console.error('Error getting access logs:', error);
+ }
+};
+
+onMounted(async () => {
+ getUser();
+ getAccessLogs();
+});
+
+
+
+
+
+
+
+ {{ user?.nickname }}
+
+ #{{ user?.id }} - {{ user.user }}
+ {{ user?.role }}
+ {{ user?.email }}
+ {{ user?.phone }}
+
+
+
+
+
+
+ {{
+ formatDateTitle(accessLog.stamp, {
+ showTime: true,
+ shortDay: true,
+ shortMonth: true,
+ showSeconds: true
+ })
+ }}
+
+
+ {{ accessLog.platform }} - {{ accessLog.browser }} -
+ {{ accessLog.version }}
+
+
+
+
+
+
diff --git a/src/pages/Admin/ConnectionsView.vue b/src/pages/Admin/ConnectionsView.vue
index befdfeb5..eec50614 100644
--- a/src/pages/Admin/ConnectionsView.vue
+++ b/src/pages/Admin/ConnectionsView.vue
@@ -91,6 +91,7 @@ onBeforeUnmount(() => clearInterval(intervalId.value));
v-else
v-for="(connection, index) in connections"
:key="index"
+ :to="{ name: 'accessLog', params: { id: connection.userId } }"
>
@@ -124,7 +125,7 @@ onBeforeUnmount(() => clearInterval(intervalId.value));
icon="people"
flat
rounded
- @click="supplantUser(connection.user)"
+ @click.stop.prevent="supplantUser(connection.user)"
>
{{ t('supplantUser') }}
@@ -132,6 +133,7 @@ onBeforeUnmount(() => clearInterval(intervalId.value));
+ {{ connections }}
diff --git a/src/pages/Admin/NewsView.vue b/src/pages/Admin/NewsView.vue
index 6bb812e6..c0f363a3 100644
--- a/src/pages/Admin/NewsView.vue
+++ b/src/pages/Admin/NewsView.vue
@@ -65,7 +65,7 @@ onMounted(async () => getNews());
{{ t('addNew') }}
-
+
{
results.forEach((result, index) => {
const fileIndex = filteredFiles[index].index;
addedFiles.value[fileIndex].uploadStatus = result.status;
+
+ if (result.status === 'rejected') {
+ addedFiles.value[fileIndex].errorMessage = t(
+ result.reason?.response?.data?.data?.message
+ );
+ }
});
const allSuccessful = results.every(
@@ -122,10 +141,10 @@ onMounted(async () => getImageCollections());
getImageCollections());
hide-upload-btn
@added="onFilesAdded"
>
-
+
getImageCollections());
:name="
statusIcons[
addedFiles[index].uploadStatus
- ]
+ ].icon
"
size="sm"
- />
+ >
+
+ {{
+ addedFiles[index].errorMessage ||
+ t(
+ statusIcons[
+ addedFiles[index]
+ .uploadStatus
+ ].tooltip
+ )
+ }}
+
+
+ >
+ {{ t('remove') }}
+
@@ -239,6 +272,8 @@ en-US:
uploadSuccess: Upload finished successfully
uploadError: Some errors happened on upload
noFilesToUpload: There are no files to upload
+ pendingUpload: Pending upload
+ imageUploaded: Image uploaded
es-ES:
collection: Colección
updateMatching: Actualizar artículos con id coincidente
@@ -248,6 +283,8 @@ es-ES:
uploadSuccess: Imágenes subidas correctamente
uploadError: Ocurrieron errores al subir alguna de las imágenes
noFilesToUpload: No se han seleccionado archivos para subir
+ pendingUpload: Subida pendiente
+ imageUploaded: Imagen subida
ca-ES:
collection: Col·lecció
updateMatching: Actualitzar els elements amb id coincident
@@ -257,6 +294,8 @@ ca-ES:
uploadSuccess: Imatges pujades correctament
uploadError: Van ocórrer errors en pujar alguna de les imatges
noFilesToUpload: No s'ha seleccionat arxius per pujar
+ pendingUpload: Pujada pendent
+ imageUploaded: Imatge pujada
fr-FR:
collection: Collection
updateMatching: Mettre à jour les éléments avec l'identifiant correspondant
@@ -266,6 +305,8 @@ fr-FR:
uploadSuccess: Les images téléchargées correctement
uploadError: Des erreurs sont survenues lors du téléchargement des images
noFilesToUpload: Aucun fichier sélectionné pour télécharger
+ pendingUpload: Téléchargement en attente
+ imageUploaded: Image téléchargée
pt-PT:
collection: Coleção
updateMatching: Atualizar itens com id correspondente
@@ -275,4 +316,6 @@ pt-PT:
uploadSuccess: Upload concluído com sucesso
uploadError: Ocorreram erros ao subir alguma das imagens
noFilesToUpload: Não há arquivos selecionados para upload
+ pendingUpload: Upload pendente
+ imageUploaded: Imagem carregada
diff --git a/src/pages/Admin/UsersView.vue b/src/pages/Admin/UsersView.vue
index b999cb97..0ba48a7e 100644
--- a/src/pages/Admin/UsersView.vue
+++ b/src/pages/Admin/UsersView.vue
@@ -9,14 +9,12 @@ import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia';
import { useUserStore } from 'stores/user';
-import useNotify from 'src/composables/useNotify.js';
const { t } = useI18n();
const router = useRouter();
const userStore = useUserStore();
const appStore = useAppStore();
const { isHeaderMounted } = storeToRefs(appStore);
-const { notify } = useNotify();
const loading = ref(false);
const users = ref([]);
@@ -37,7 +35,6 @@ const supplantUser = async user => {
router.push({ name: 'confirmedOrders' });
} catch (error) {
console.error('Error supplanting user:', error);
- notify(error.message, 'negative');
}
};
@@ -67,7 +64,7 @@ const supplantUser = async user => {
v-else
v-for="(user, index) in users"
:key="index"
- :clickable="false"
+ :to="{ name: 'accessLog', params: { id: user.id } }"
>
@@ -77,14 +74,17 @@ const supplantUser = async user => {
- {{ t('Impersonate user') }}
-
+
+ {{ t('Impersonate user') }}
+
+
+ {{ t('Disabled') }}
diff --git a/src/pages/Ecomerce/PendingOrders.vue b/src/pages/Ecomerce/PendingOrders.vue
index dbf7acca..38053da8 100644
--- a/src/pages/Ecomerce/PendingOrders.vue
+++ b/src/pages/Ecomerce/PendingOrders.vue
@@ -85,9 +85,7 @@ onMounted(async () => {
>
- {{
- formatDateTitle(order.sent)
- }}
+ {{ formatDateTitle(order.sent) }}
#{{ order.id }}
{{ order.nickname }}
@@ -106,13 +104,17 @@ onMounted(async () => {
() => removeOrder(order.id, index)
)
"
- />
+ >
+ {{ t('deleteOrder') }}
+
+ >
+ {{ t('loadOrderIntoCart') }}
+
@@ -124,16 +126,26 @@ onMounted(async () => {
en-US:
newOrder: New order
areYouSureDeleteOrder: Are you sure you want to delete the order?
+ deleteOrder: Delete order
+ loadOrderIntoCart: Load order into cart
es-ES:
newOrder: Nuevo pedido
areYouSureDeleteOrder: ¿Seguro que quieres borrar el pedido?
+ deleteOrder: Eliminar pedido
+ loadOrderIntoCart: Cargar pedido en la cesta
ca-ES:
newOrder: Nova comanda
areYouSureDeleteOrder: Segur que vols esborrar la comanda?
+ deleteOrder: Eliminar comanda
+ loadOrderIntoCart: Carregar comanda a la cistella
fr-FR:
newOrder: Nouvelle commande
areYouSureDeleteOrder: Êtes-vous sûr de vouloir supprimer la commande?
+ deleteOrder: Supprimer la commande
+ loadOrderIntoCart: Charger la commande dans le panier
pt-PT:
newOrder: Novo pedido
areYouSureDeleteOrder: Tem certeza de que deseja excluir o pedido?
+ deleteOrder: Excluir pedido
+ loadOrderIntoCart: Carregar pedido no carrinho
diff --git a/src/router/routes.js b/src/router/routes.js
index e238a700..fa9dbd83 100644
--- a/src/router/routes.js
+++ b/src/router/routes.js
@@ -143,12 +143,20 @@ const routes = [
},
{
name: 'adminUsers',
- path: 'admin/users',
+ path: 'admin/users:/:id?',
meta: {
title: 'Users'
},
component: () => import('pages/Admin/UsersView.vue')
},
+ {
+ name: 'accessLog',
+ path: 'admin/access-log/:id?',
+ meta: {
+ title: 'Access log'
+ },
+ component: () => import('pages/Admin/AccessLogView.vue')
+ },
{
name: 'adminConnections',
path: 'admin/connections',