Procházet zdrojové kódy

feat: tela de contratos (Visualizacao em lista)

ebagabee před 1 měsícem
rodič
revize
8065c2e90a

+ 5 - 0
src/api/studentContract.js

@@ -5,6 +5,11 @@ export const getStudentContracts = async (studentId) => {
   return data.payload;
 };
 
+export const getAllContracts = async () => {
+  const { data } = await api.get("/student-contract");
+  return data.payload;
+};
+
 export const createStudentContract = async (payload) => {
   const { data } = await api.post("/student-contract", payload);
   return data.payload;

+ 129 - 0
src/pages/contracts/ContractsPage.vue

@@ -0,0 +1,129 @@
+<template>
+  <div>
+    <DefaultHeaderPage title="Contratos" />
+
+    <div class="q-px-sm">
+      <div class="stat-cards-row q-mb-md">
+        <DashboardStatCard
+          title="Contratos Ativos"
+          icon="mdi-file-check-outline"
+          :value="String(metrics.active)"
+        />
+        <DashboardStatCard
+          title="Contratos Congelados"
+          icon="mdi-snowflake"
+          :value="String(metrics.frozen)"
+        />
+        <DashboardStatCard
+          title="Contratos Cancelados"
+          icon="mdi-file-remove-outline"
+          :value="String(metrics.cancelled)"
+        />
+        <DashboardStatCard
+          title="Contratos Inadimplentes"
+          icon="mdi-alert-circle-outline"
+          value="0"
+        />
+      </div>
+
+      <DefaultTable
+        v-model:rows="rows"
+        title="Contratos"
+        :columns
+        descricao="contratos"
+        :feminino="false"
+        no-api-call
+        :loading="isLoading"
+      >
+        <template #body-cell-contato="{ row }">
+          <q-td>
+            <div>{{ row.student_phone }}</div>
+            <div class="text-grey-6 text-caption">
+              {{ row.student_city }}<template v-if="row.student_city && row.student_state_code">, </template>{{ row.student_state_code }}
+            </div>
+          </q-td>
+        </template>
+
+        <template #body-cell-status="{ row }">
+          <q-td align="center">
+            <q-badge
+              :color="statusColor(row.status)"
+              :label="statusLabel(row.status)"
+            />
+          </q-td>
+        </template>
+      </DefaultTable>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from "vue";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+import DefaultTable from "src/components/defaults/DefaultTable.vue";
+import DashboardStatCard from "src/components/charts/DashboardStatCard.vue";
+import { getAllContracts } from "src/api/studentContract";
+
+const rows = ref([]);
+const isLoading = ref(false);
+
+const metrics = computed(() => ({
+  active: rows.value.filter((r) => r.status === "active").length,
+  frozen: rows.value.filter((r) => r.status === "frozen").length,
+  cancelled: rows.value.filter((r) => r.status === "cancelled").length,
+}));
+
+const columns = ref([
+  { name: "name", label: "Nome", field: "student_name", align: "left" },
+  { name: "contato", label: "Contato/Endereço", field: null, align: "left" },
+  { name: "status", label: "Status Contrato", field: "status", align: "center" },
+]);
+
+async function loadContracts() {
+  isLoading.value = true;
+  try {
+    rows.value = await getAllContracts();
+  } finally {
+    isLoading.value = false;
+  }
+}
+
+function statusColor(status) {
+  if (status === "active") return "positive";
+  if (status === "frozen") return "info";
+  if (status === "cancelled") return "negative";
+  return "warning";
+}
+
+function statusLabel(status) {
+  if (status === "active") return "Ativo";
+  if (status === "frozen") return "Congelado";
+  if (status === "cancelled") return "Cancelado";
+  return "Inativo";
+}
+
+onMounted(loadContracts);
+</script>
+
+<style scoped lang="scss">
+.stat-cards-row {
+  display: flex;
+  flex-wrap: nowrap;
+  gap: 12px;
+}
+
+.stat-cards-row > * {
+  flex: 1 1 0;
+  min-width: 0;
+}
+
+@media (max-width: 768px) {
+  .stat-cards-row {
+    flex-wrap: wrap;
+  }
+
+  .stat-cards-row > * {
+    flex: 1 1 calc(50% - 8px);
+  }
+}
+</style>

+ 15 - 0
src/router/routes/config.route.js

@@ -1,4 +1,19 @@
 export default [
+  {
+    path: "/contracts",
+    name: "ContractsPage",
+    component: () => import("pages/contracts/ContractsPage.vue"),
+    meta: {
+      title: "Contratos",
+      requireAuth: true,
+      breadcrumbs: [
+        {
+          name: "ContractsPage",
+          title: "Contratos",
+        },
+      ],
+    },
+  },
   {
     path: "/students",
     name: "StudentPage",

+ 8 - 0
src/stores/navigation.js

@@ -20,6 +20,14 @@ export const navigationStore = defineStore("navigation", () => {
       disable: false,
       permission: true,
     },
+    {
+      type: "single",
+      title: "Contratos",
+      name: "ContractsPage",
+      icon: "mdi-file-document-outline",
+      disable: false,
+      permission: true,
+    },
     {
       type: "single",
       title: "Pacotes",