Bläddra i källkod

feat: add alerta com ultima sincronizacao do stays

Gustavo Mantovani 2 dagar sedan
förälder
incheckning
beaf5356f1

+ 2 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "alugap",
-  "version": "0.5.0",
+  "version": "0.1.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "alugap",
-      "version": "0.5.0",
+      "version": "0.1.0",
       "dependencies": {
         "@quasar/extras": "^1.16.17",
         "axios": "^1.8.4",

+ 11 - 0
src/api/syncExecution.js

@@ -0,0 +1,11 @@
+import { api } from "src/boot/axios";
+
+export const getSyncExecutions = async () => {
+  const { data } = await api.get("/sync-execution");
+  return data.payload;
+};
+
+export const getSyncExecution = async (id) => {
+  const { data } = await api.get(`/sync-execution/${id}`);
+  return data.payload;
+};

+ 45 - 12
src/components/layout/DefaultHeaderPage.vue

@@ -1,19 +1,52 @@
 <template>
   <div>
-    <q-breadcrumbs class="q-mb-md">
-      <template #separator> <div class="text-primary">/</div> </template>
+    <div
+      class="row items-baseline q-mb-md no-wrap"
+      style="line-height: 1.4"
+    >
+      <div style="min-width: 140px">
+        <q-breadcrumbs>
+          <template #separator>
+            <div class="text-primary">/</div>
+          </template>
 
-      <q-breadcrumbs-el
-        v-for="breadcrumb in $route.meta?.breadcrumbs"
-        :key="breadcrumb?.name"
-        :label="$t(breadcrumb?.title)"
-        :to="{ name: breadcrumb?.name }"
-        style="font-size: 12px"
-      />
-    </q-breadcrumbs>
+          <q-breadcrumbs-el
+            v-for="breadcrumb in $route.meta?.breadcrumbs"
+            :key="breadcrumb?.name"
+            :label="$t(breadcrumb?.title)"
+            :to="{ name: breadcrumb?.name }"
+            style="font-size: 12px"
+          />
+        </q-breadcrumbs>
+      </div>
 
-    <span class="text-h6 text-bold">{{ $t($route.meta?.title) }}</span>
+      <span
+        v-if="syncExecution.lastSyncExecution"
+        class="text-primary text-weight-bold sync-label q-ml-md"
+      >
+        {{ $t("ui.sync.stays_sync") }}:
+        {{ convertDateTime(syncExecution.lastSyncExecution.finished_at) }}
+      </span>
+    </div>
+
+    <span class="text-h6 text-bold">
+      {{ $t($route.meta?.title) }}
+    </span>
+
+    <q-separator class="q-my-sm" />
   </div>
 </template>
 
-<script setup></script>
+<script setup>
+import { convertDateTime } from "src/helpers/utils";
+import { syncExecutionStore } from "src/stores/syncExecution";
+
+const syncExecution = syncExecutionStore();
+</script>
+
+<style scoped>
+.sync-label {
+  font-size: 13px;
+  white-space: nowrap;
+}
+</style>

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

@@ -255,6 +255,10 @@
       "state": "State",
       "country": "Country",
       "exit": "Exit"
+    },
+    "sync": {
+      "last_sync": "Last sync",
+      "stays_sync": "Stays sync"
     }
   },
   "property": {

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

@@ -258,6 +258,10 @@
       "state": "Estado/Provincia",
       "country": "País",
       "exit": "Salir"
+    },
+    "sync": {
+      "last_sync": "Última sincronización",
+      "stays_sync": "Sincronización de Stays"
     }
   },
   "property": {

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

@@ -255,6 +255,10 @@
       "state": "Estado",
       "country": "País",
       "exit": "Sair"
+    },
+    "sync": {
+      "last_sync": "Última sincronização",
+      "stays_sync": "Sincronização do Stays"
     }
   },
   "property": {

+ 35 - 17
src/layouts/MainLayout.vue

@@ -9,22 +9,29 @@
         class="flex justify-between bg-surface"
         style="border-radius: 6px !important"
       >
-        <q-btn dense flat @click="toggleLeftDrawer">
-          <q-icon name="menu" :color="$q.dark.isActive ? 'white' : 'black'" />
+        <q-btn
+          dense
+          flat
+          @click="toggleLeftDrawer"
+        >
+          <q-icon
+            name="menu"
+            :color="$q.dark.isActive ? 'white' : 'black'"
+          />
         </q-btn>
 
         <q-btn dense flat>
           <img
-            :src="someAvatar()"
             alt="avatar"
             style="width: 20px; height: 20px; border-radius: 50%"
+            :src="someAvatar()"
           />
 
           <q-menu anchor="center right" self="top start">
             <q-list class="column no-wrap overflow-hidden">
               <q-item
-                v-ripple
                 v-close-popup
+                v-ripple
                 clickable
                 exact
                 exact-active-class="menu-selected"
@@ -39,13 +46,17 @@
                     />
                   </q-item-section>
 
-                  <q-item-section>{{
-                    $t("ui.navigation.profile")
-                  }}</q-item-section>
+                  <q-item-section>
+                    {{ $t("user.profile.singular") }}
+                  </q-item-section>
                 </div>
               </q-item>
 
-              <q-item v-ripple clickable @click="logoutFn">
+              <q-item
+                v-ripple
+                clickable
+                @click="logoutFn"
+              >
                 <div class="flex">
                   <q-item-section avatar>
                     <q-icon
@@ -55,7 +66,9 @@
                     />
                   </q-item-section>
 
-                  <q-item-section>{{ $t("auth.logout") }}</q-item-section>
+                  <q-item-section>
+                    {{ $t("auth.logout") }}
+                  </q-item-section>
                 </div>
               </q-item>
             </q-list>
@@ -90,9 +103,10 @@
 </template>
 
 <script setup>
-import { ref, useTemplateRef, watch } from "vue";
-import { useRoute } from "vue-router";
+import { onMounted, ref, useTemplateRef, watch } from "vue";
+import { syncExecutionStore } from "src/stores/syncExecution";
 import { useAuth } from "src/composables/useAuth";
+import { useRoute } from "vue-router";
 import { useRouter } from "vue-router";
 
 import LeftMenuLayout from "src/components/layout/LeftMenuLayout.vue";
@@ -106,23 +120,23 @@ const { logout } = useAuth();
 
 const route = useRoute();
 const router = useRouter();
-
 const scrollAreaRef = useTemplateRef("scrollAreaRef");
 
+const syncExecution = syncExecutionStore();
+
 const leftDrawerOpen = ref(false);
 
 let oldValue = route.path;
 
-const someAvatar = () => {
-  return "https://cdn.quasar.dev/img/avatar4.jpg";
-};
-
 const logoutFn = async () => {
   await logout();
-
   router.push({ name: "LoginPage" });
 };
 
+const someAvatar = () => {
+  return "https://cdn.quasar.dev/img/avatar4.jpg";
+};
+
 const toggleLeftDrawer = () => {
   leftDrawerOpen.value = !leftDrawerOpen.value;
 };
@@ -135,6 +149,10 @@ watch(route, (value) => {
 
   oldValue = value.path;
 });
+
+onMounted(() => {
+  syncExecution.fetchLastSyncExecution();
+});
 </script>
 <style scoped>
 .v-enter-active {

+ 28 - 0
src/stores/syncExecution.js

@@ -0,0 +1,28 @@
+import { defineStore } from "pinia";
+import { getSyncExecutions } from "src/api/syncExecution";
+import { ref } from "vue";
+
+export const syncExecutionStore = defineStore("syncExecution", () => {
+  const lastSyncExecution = ref(null);
+
+  const fetchLastSyncExecution = async () => {
+    try {
+      const executions = await getSyncExecutions();
+
+      if (executions && executions.length > 0) {
+        const lastSuccess = [...executions]
+          .reverse()
+          .find((e) => e.status === "success");
+
+        lastSyncExecution.value = lastSuccess || null;
+      }
+    } catch {
+      lastSyncExecution.value = null;
+    }
+  };
+
+  return {
+    lastSyncExecution,
+    fetchLastSyncExecution,
+  };
+});