Jelajahi Sumber

bloco log de acessos na dashboard

Gustavo Zanatta 1 bulan lalu
induk
melakukan
754438d98d

+ 2 - 0
src/api/userAccessLog.js

@@ -6,11 +6,13 @@ export const getAccessLogsPaginated = async ({
   type,
   date_from,
   date_to,
+  last_per_user,
 } = {}) => {
   const params = { page, per_page: perPage };
   if (type) params.type = type;
   if (date_from) params.date_from = date_from;
   if (date_to) params.date_to = date_to;
+  if (last_per_user) params.last_per_user = true;
 
   const { data } = await api.get("/user-access-log", { params });
   return { data: { result: data.payload } };

+ 1 - 0
src/i18n/locales/en.json

@@ -515,6 +515,7 @@
       "contratos_a_vencer": "Expiring Contracts",
       "novos_mes": "New (month)",
       "associados_pendentes": "Pending Members",
+      "ultimos_acessos": "Last Accesses",
       "association_date": "Association Date",
       "registration_date": "Registration Date"
     },

+ 1 - 0
src/i18n/locales/es.json

@@ -515,6 +515,7 @@
       "contratos_a_vencer": "Contratos por Vencer",
       "novos_mes": "Nuevos (mes)",
       "associados_pendentes": "Asociados Pendientes",
+      "ultimos_acessos": "Últimos Accesos",
       "association_date": "Fecha de Asociación",
       "registration_date": "Fecha de Registro"
     },

+ 1 - 0
src/i18n/locales/pt.json

@@ -515,6 +515,7 @@
       "contratos_a_vencer": "Contratos a Vencer",
       "novos_mes": "Novos (mês)",
       "associados_pendentes": "Associados Pendentes",
+      "ultimos_acessos": "Últimos Acessos",
       "association_date": "Data Associação",
       "registration_date": "Data Cadastro"
     },

+ 38 - 1
src/pages/dashboard/DashboardPage.vue

@@ -12,7 +12,7 @@
           <div
             v-for="card in statCards"
             :key="card.key"
-            class="col-12 col-sm-6 col-md-4 col-lg-2"
+            class="col-12 col-sm-6 col-md-4 col-lg"
           >
             <q-card
               flat
@@ -40,6 +40,7 @@
             :api-call="activeApiCall"
             :add-item="false"
             sem-table-top
+            :show-search-field="activeCard !== 'ultimos_acessos'"
           >
             <template #body-cell-status="{ row }">
               <q-td>
@@ -51,6 +52,22 @@
                 />
               </q-td>
             </template>
+
+            <template #body-cell-user_type="{ row }">
+              <q-td class="text-center">
+                <q-badge
+                  outline
+                  :color="row.user_type === 'associado' ? 'violet-normal' : 'teal'"
+                  :label="row.user_type === 'associado' ? $t('auth.type.associado') : $t('auth.type.parceiro')"
+                />
+              </q-td>
+            </template>
+
+            <template #body-cell-accessed_at="{ row }">
+              <q-td>
+                {{ formatDateTime(row.accessed_at) }}
+              </q-td>
+            </template>
           </DefaultTableServerSide>
         </q-card>
       </template>
@@ -66,6 +83,7 @@ import DefaultTableServerSide from "src/components/defaults/DefaultTableServerSi
 import { getDashboardStats } from "src/api/dashboard";
 import { getUsersPaginated } from "src/api/user";
 import { getPartnerAgreementsPaginated, getExpiringPartnerAgreementsPaginated } from "src/api/partnerAgreement";
+import { getAccessLogsPaginated } from "src/api/userAccessLog";
 import { formatDateYMDtoDMY, getStatusColor, getStatusI18nKey } from "src/helpers/utils";
 
 const { t } = useI18n();
@@ -81,6 +99,7 @@ const statCards = [
   { key: "contratos_a_vencer",   icon: "mdi-file-clock",     labelKey: "dashboard.stats.contratos_a_vencer" },
   { key: "novos_mes",            icon: "mdi-account-plus",   labelKey: "dashboard.stats.novos_mes" },
   { key: "associados_pendentes", icon: "mdi-account-alert",  labelKey: "dashboard.stats.associados_pendentes" },
+  { key: "ultimos_acessos",      icon: "mdi-login-variant",  labelKey: "dashboard.stats.ultimos_acessos" },
 ];
 
 // Colunas por tipo de card
@@ -105,10 +124,17 @@ const columnsContratosAVencer = computed(() => [
   { name: "status",       label: t("common.terms.status"),     field: "status",        align: "left" },
 ]);
 
+const columnsAccessLogs = computed(() => [
+  { name: "user_name",   label: t("access_log.user"),        field: "user_name",   align: "left",   sortable: false },
+  { name: "user_type",   label: t("access_log.user_type"),   field: "user_type",   align: "center", sortable: false },
+  { name: "accessed_at", label: t("access_log.accessed_at"), field: "accessed_at", align: "left",   sortable: false },
+]);
+
 const tableColumns = computed(() => {
   if (!activeCard.value) return [];
   if (activeCard.value === "contratos_a_vencer") return columnsContratosAVencer.value;
   if (activeCard.value === "parceiros" || activeCard.value === "novos_mes") return columnsParceiros.value;
+  if (activeCard.value === "ultimos_acessos") return columnsAccessLogs.value;
   return columnsAssociados.value;
 });
 
@@ -127,11 +153,22 @@ const activeApiCall = computed(() => {
       return (p) => getExpiringPartnerAgreementsPaginated({ ...p });
     case "novos_mes":
       return (p) => getPartnerAgreementsPaginated({ ...p, createdMonth: "current" });
+    case "ultimos_acessos":
+      return (p) => getAccessLogsPaginated({ ...p, last_per_user: true });
     default:
       return null;
   }
 });
 
+const formatDateTime = (isoString) => {
+  if (!isoString) return "—";
+  const d = new Date(isoString);
+  return d.toLocaleString("pt-BR", {
+    day: "2-digit", month: "2-digit", year: "numeric",
+    hour: "2-digit", minute: "2-digit", second: "2-digit",
+  });
+};
+
 const onCardClick = (card) => {
   activeCard.value = activeCard.value === card.key ? null : card.key;
 };

+ 1 - 0
src/pages/gestao-associados/components/AccessLogsTable.vue

@@ -184,6 +184,7 @@ const apiCallWithFilters = (params) =>
     type: filters.value.type || undefined,
     date_from: filters.value.date_from || undefined,
     date_to: filters.value.date_to || undefined,
+    last_per_user: true,
   });
 
 const onFilterChange = () => {