Kaynağa Gözat

refactor: traduz titulos, function e arquivos de portugues para ingles.

ebagabee 1 ay önce
ebeveyn
işleme
0a5c8a9eec

+ 15 - 0
src/api/student.js

@@ -4,3 +4,18 @@ export const getStudents = async () => {
   const { data } = await api.get("/student");
   return data.payload;
 };
+
+export const getStudentSummaryFranchisor = async () => {
+  const { data } = await api.get("/student/franchisor/summary");
+  return data.payload;
+};
+
+export const getFranchisorActiveStudents = async () => {
+  const { data } = await api.get("/student/franchisor/active");
+  return data.payload;
+};
+
+export const getFranchisorStudentDetail = async (id) => {
+  const { data } = await api.get(`/student/franchisor/${id}`);
+  return data.payload;
+};

+ 0 - 0
src/components/charts/AniversariantesCard.vue → src/components/charts/BirthdaysCard.vue


+ 6 - 6
src/components/charts/FeriadosCard.vue → src/components/charts/HolidaysCard.vue

@@ -1,5 +1,5 @@
 <template>
-  <q-card flat class="feriados-card card-ring">
+  <q-card flat class="holidays-card card-ring">
     <div class="flex justify-between items-center no-wrap q-mb-sm">
       <div class="flex items-center q-gutter-x-sm">
         <q-icon name="mdi-calendar-star-outline" color="dark" size="sm" />
@@ -45,8 +45,8 @@
 <script setup>
 import { ref, computed, onMounted } from "vue";
 import { useQuasar } from "quasar";
-import FeriadosDialog from "src/pages/dashboard/components/FeriadosDialog.vue";
-import FeriadosEditDialog from "src/pages/dashboard/components/FeriadosEditDialog.vue";
+import HolidaysDialog from "src/pages/dashboard/components/HolidaysDialog.vue";
+import HolidayEditDialog from "src/pages/dashboard/components/HolidayEditDialog.vue";
 import { getBaseHolidays } from "src/api/holiday";
 
 const $q = useQuasar();
@@ -82,7 +82,7 @@ function typeLabel(type) {
 
 function openDialog() {
   $q.dialog({
-    component: FeriadosDialog,
+    component: HolidaysDialog,
     componentProps: { initialHolidays: holidays.value },
   }).onOk((updated) => {
     holidays.value = updated;
@@ -91,7 +91,7 @@ function openDialog() {
 
 function openEdit(holiday) {
   $q.dialog({
-    component: FeriadosEditDialog,
+    component: HolidayEditDialog,
     componentProps: { holiday },
   }).onOk(({ action, holiday: updated, id }) => {
     if (action === "update") {
@@ -105,7 +105,7 @@ function openEdit(holiday) {
 </script>
 
 <style scoped>
-.feriados-card {
+.holidays-card {
   border-radius: 12px;
   padding: 20px 24px;
   display: flex;

+ 39 - 31
src/pages/dashboard/DashboardPage.vue

@@ -56,10 +56,10 @@
         <DashboardStatCard
           title="Total alunos (contratos ativos)"
           icon="mdi-account-multiple-outline"
-          value="4.527"
-          badge="3.200 ativos"
+          :value="totalStudents"
+          :badge="`${activeContracts} ativos`"
           clickable
-          @click="openAlunosDialog"
+          @click="openActiveStudentsDialog"
         />
         <DashboardStatCard
           title="Contratos Congelados"
@@ -67,7 +67,7 @@
           value="57"
           subtitle="É hora de incentivar nossos alunos"
           clickable
-          @click="openContratosCongeladosDialog"
+          @click="openFrozenContractsDialog"
         />
         <DashboardStatCard
           title="Contratos Cancelados"
@@ -75,7 +75,7 @@
           value="57"
           subtitle="É hora de incentivar nossos alunos"
           clickable
-          @click="openContratosCanceladosDialog"
+          @click="openCancelledContractsDialog"
         />
         <DashboardStatCard
           title="Receita Geral"
@@ -94,14 +94,14 @@
           badge-color="approved"
           custom-style="padding: 6px 24px"
           clickable
-          @click="openFrequenciaMediaDialog"
+          @click="openAverageAttendanceDialog"
         />
         <DashboardStatCard
           title="Estoque Geral de Produtos"
           icon="mdi-currency-usd"
           value="56"
           clickable
-          @click="openEstoqueProdutosDialog"
+          @click="openProductStockDialog"
         />
         <DashboardStatCard
           title="Tarefas Pendentes"
@@ -115,7 +115,7 @@
           value="2"
           subtitle="Estável"
           clickable
-          @click="openTicketsAbertoDialog"
+          @click="openTicketsDialog"
         />
       </div>
 
@@ -144,8 +144,8 @@
           />
         </DashboardChartCard>
 
-        <FeriadosCard />
-        <AniversariantesCard :people="aniversariantes" />
+        <HolidaysCard />
+        <BirthdaysCard :people="birthdays" />
       </div>
     </div>
 
@@ -166,14 +166,15 @@ import UnitSelect from "src/components/selects/UnitSelect.vue";
 import DashboardStatCard from "src/components/charts/DashboardStatCard.vue";
 import DashboardChartCard from "src/components/charts/DashboardChartCard.vue";
 import GroupedBarChart from "src/components/charts/normal/GroupedBarChart.vue";
-import AniversariantesCard from "src/components/charts/AniversariantesCard.vue";
-import FeriadosCard from "src/components/charts/FeriadosCard.vue";
-import AlunosAtivosDialog from "src/pages/dashboard/components/AlunosAtivosDialog.vue";
-import ContratosCongeladosDialog from "src/pages/dashboard/components/ContratosCongeladosDialog.vue";
-import ContratosCanceladosDialog from "src/pages/dashboard/components/ContratosCanceladosDialog.vue";
-import FrequenciaMediaDialog from "src/pages/dashboard/components/FrequenciaMediaDialog.vue";
-import EstoqueProdutosDialog from "src/pages/dashboard/components/EstoqueProdutosDialog.vue";
-import TicketsAbertoDialog from "src/pages/dashboard/components/TicketsAbertoDialog.vue";
+import BirthdaysCard from "src/components/charts/BirthdaysCard.vue";
+import HolidaysCard from "src/components/charts/HolidaysCard.vue";
+import ActiveStudentsDialog from "src/pages/dashboard/components/ActiveStudentsDialog.vue";
+import { getStudentSummaryFranchisor } from "src/api/student";
+import FrozenContractsDialog from "src/pages/dashboard/components/FrozenContractsDialog.vue";
+import CancelledContractsDialog from "src/pages/dashboard/components/CancelledContractsDialog.vue";
+import AverageAttendanceDialog from "src/pages/dashboard/components/AverageAttendanceDialog.vue";
+import ProductStockDialog from "src/pages/dashboard/components/ProductStockDialog.vue";
+import OpenTicketsDialog from "src/pages/dashboard/components/OpenTicketsDialog.vue";
 
 const { t } = useI18n();
 
@@ -181,28 +182,31 @@ const $q = useQuasar();
 
 const isLoading = ref(true);
 
-const openAlunosDialog = () => {
-  $q.dialog({ component: AlunosAtivosDialog });
+const totalStudents = ref(0);
+const activeContracts = ref(0);
+
+const openActiveStudentsDialog = () => {
+  $q.dialog({ component: ActiveStudentsDialog });
 };
 
-const openContratosCongeladosDialog = () => {
-  $q.dialog({ component: ContratosCongeladosDialog });
+const openFrozenContractsDialog = () => {
+  $q.dialog({ component: FrozenContractsDialog });
 };
 
-const openContratosCanceladosDialog = () => {
-  $q.dialog({ component: ContratosCanceladosDialog });
+const openCancelledContractsDialog = () => {
+  $q.dialog({ component: CancelledContractsDialog });
 };
 
-const openFrequenciaMediaDialog = () => {
-  $q.dialog({ component: FrequenciaMediaDialog });
+const openAverageAttendanceDialog = () => {
+  $q.dialog({ component: AverageAttendanceDialog });
 };
 
-const openEstoqueProdutosDialog = () => {
-  $q.dialog({ component: EstoqueProdutosDialog });
+const openProductStockDialog = () => {
+  $q.dialog({ component: ProductStockDialog });
 };
 
-const openTicketsAbertoDialog = () => {
-  $q.dialog({ component: TicketsAbertoDialog });
+const openTicketsDialog = () => {
+  $q.dialog({ component: OpenTicketsDialog });
 };
 const defaultPeriod = ref("month");
 const defaultEventId = ref(1);
@@ -221,7 +225,7 @@ const periodOptions = [
   { label: "Personalizado", value: "custom" },
 ];
 
-const aniversariantes = [
+const birthdays = [
   { day: 10, name: "Heloisa Faria" },
   { day: 11, name: "Juliana Costa" },
   { day: 16, name: "Juliana Costa" },
@@ -437,6 +441,10 @@ watch([defaultPeriod, defaultEventId], async () => {
 });
 
 onMounted(async () => {
+  getStudentSummaryFranchisor().then((summary) => {
+    totalStudents.value = summary.total;
+    activeContracts.value = summary.active;
+  });
   await updateDashboardData();
 });
 </script>

+ 72 - 0
src/pages/dashboard/StudentDetailPage.vue

@@ -0,0 +1,72 @@
+<template>
+  <div>
+    <DefaultHeaderPage title="Student Detail" />
+
+    <div class="q-pa-md">
+      <div v-if="loading" class="flex flex-center q-pa-xl">
+        <q-spinner color="primary" size="50px" />
+      </div>
+
+      <div v-else-if="student" class="column q-gutter-md" style="max-width: 600px">
+        <q-card flat bordered style="border-radius: 12px">
+          <q-card-section>
+            <div class="text-subtitle2 text-grey-6 q-mb-xs">Nome do Aluno</div>
+            <div class="text-h6 text-dark">{{ student.name }}</div>
+          </q-card-section>
+
+          <q-separator inset />
+
+          <q-card-section>
+            <div class="text-subtitle2 text-grey-6 q-mb-xs">Contato / Unidade</div>
+            <div class="text-body1 text-dark">{{ student.phone ?? "—" }}</div>
+            <div class="text-caption text-grey-6">
+              {{ student.unit?.fantasy_name ?? "—" }}
+            </div>
+          </q-card-section>
+
+          <q-separator inset />
+
+          <q-card-section>
+            <div class="text-subtitle2 text-grey-6 q-mb-xs">Protocolo do Contrato</div>
+            <div class="text-body1 text-dark">
+              {{ student.protocol ?? "—" }}
+            </div>
+          </q-card-section>
+        </q-card>
+
+        <div>
+          <q-btn
+            flat
+            color="primary"
+            icon="mdi-arrow-left"
+            label="Voltar"
+            @click="$router.back()"
+          />
+        </div>
+      </div>
+
+      <div v-else class="text-grey-6 q-pa-md">Student not found.</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import { useRoute } from "vue-router";
+import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
+import { getFranchisorStudentDetail } from "src/api/student";
+
+const route = useRoute();
+const loading = ref(true);
+const student = ref(null);
+
+onMounted(async () => {
+  try {
+    student.value = await getFranchisorStudentDetail(route.params.id);
+  } catch {
+    // silent
+  } finally {
+    loading.value = false;
+  }
+});
+</script>

+ 37 - 44
src/pages/dashboard/components/AlunosAtivosDialog.vue → src/pages/dashboard/components/ActiveStudentsDialog.vue

@@ -2,9 +2,7 @@
   <q-dialog ref="dialogRef" @hide="onDialogHide">
     <q-card style="width: 800px; max-width: 95vw; border-radius: 12px">
       <q-bar class="bg-transparent q-px-md" style="height: 55px">
-        <span class="text-h6 text-dark" style="font-weight: 600"
-          >Alunos Ativos</span
-        >
+        <span class="text-h6 text-dark" style="font-weight: 600">Active Students</span>
         <q-space />
         <q-btn dense flat icon="mdi-close" @click="onDialogCancel" />
       </q-bar>
@@ -12,11 +10,11 @@
       <q-card-section class="q-pt-none q-pb-md q-px-md">
         <q-card flat bordered style="border-radius: 8px">
           <q-card-section class="q-pb-xs">
-            <div class="text-subtitle2 text-dark">Lista de alunos</div>
+            <div class="text-subtitle2 text-dark">Active students list</div>
             <div class="text-caption text-grey-6">
-              {{ alunos.length }} Alunos Cadastrados
+              {{ students.length }} Active Students
+              <q-spinner v-if="loading" size="16px" color="primary" class="q-ml-sm" />
             </div>
-            <q-spinner v-if="loading" size="16px" color="primary" class="q-ml-sm" />
           </q-card-section>
 
           <q-card-section class="q-pt-xs q-pb-sm">
@@ -24,7 +22,7 @@
               v-model="search"
               dense
               borderless
-              placeholder="Busque por status, nome, tel/whats"
+              placeholder="Search by name, phone or unit"
             >
               <template #prepend>
                 <q-icon name="mdi-magnify" color="grey-6" />
@@ -43,24 +41,19 @@
           <q-separator />
 
           <div style="max-height: 320px; overflow-y: auto">
-            <template v-for="(aluno, index) in filteredAlunos" :key="aluno.id">
+            <template v-for="(student, index) in filteredStudents" :key="student.id">
               <div
                 class="list-row q-px-md q-py-sm"
-                :class="{ 'row-selected': index === selectedIndex }"
-                @click="selectedIndex = index"
+                @click="goToDetail(student)"
               >
-                <span class="text-body2 text-dark">{{ aluno.nome }}</span>
+                <span class="text-body2 text-dark">{{ student.name }}</span>
                 <div class="column" style="gap: 2px">
-                  <span class="text-caption text-dark">{{
-                    aluno.telefone
-                  }}</span>
-                  <span class="text-caption text-grey-6">{{
-                    aluno.unidade
-                  }}</span>
+                  <span class="text-caption text-dark">{{ student.phone }}</span>
+                  <span class="text-caption text-grey-6">{{ student.unit }}</span>
                 </div>
                 <q-badge
-                  :label="aluno.status"
-                  :color="aluno.status === 'Ativo' ? 'btn-badge' : 'grey'"
+                  label="Ativo"
+                  color="btn-badge"
                   style="
                     border-radius: 8px;
                     font-size: 11px;
@@ -70,7 +63,7 @@
                   "
                 />
               </div>
-              <q-separator v-if="index < filteredAlunos.length - 1" />
+              <q-separator v-if="index < filteredStudents.length - 1" />
             </template>
           </div>
         </q-card>
@@ -91,47 +84,51 @@
 <script setup>
 import { ref, computed, onMounted } from "vue";
 import { useDialogPluginComponent } from "quasar";
-import { getStudents } from "src/api/student";
+import { useRouter } from "vue-router";
+import { getFranchisorActiveStudents } from "src/api/student";
 
 defineEmits([...useDialogPluginComponent.emits]);
 
-const { dialogRef, onDialogHide, onDialogCancel } = useDialogPluginComponent();
+const { dialogRef, onDialogHide, onDialogCancel, onDialogOK } =
+  useDialogPluginComponent();
 
+const router = useRouter();
 const search = ref("");
-const selectedIndex = ref(0);
 const loading = ref(false);
-
-const alunos = ref([]);
+const students = ref([]);
 
 onMounted(async () => {
   loading.value = true;
   try {
-    const students = await getStudents();
-    alunos.value = students.map((s) => ({
+    const data = await getFranchisorActiveStudents();
+    students.value = data.map((s) => ({
       id: s.id,
-      nome: s.name,
-      telefone: s.phone ?? "—",
-      unidade: s.unit?.fantasy_name ?? "—",
-      status: s.status === "active" ? "Ativo" : "Inativo",
+      name: s.name,
+      phone: s.phone ?? "—",
+      unit: s.unit?.fantasy_name ?? "—",
     }));
   } catch {
-    // silencioso
+    // silent
   } finally {
     loading.value = false;
   }
 });
 
-const filteredAlunos = computed(() => {
-  if (!search.value) return alunos.value;
+const filteredStudents = computed(() => {
+  if (!search.value) return students.value;
   const q = search.value.toLowerCase();
-  return alunos.value.filter(
-    (a) =>
-      a.nome.toLowerCase().includes(q) ||
-      a.telefone.includes(q) ||
-      a.unidade.toLowerCase().includes(q) ||
-      a.status.toLowerCase().includes(q),
+  return students.value.filter(
+    (s) =>
+      s.name.toLowerCase().includes(q) ||
+      s.phone.includes(q) ||
+      s.unit.toLowerCase().includes(q),
   );
 });
+
+const goToDetail = (student) => {
+  onDialogOK();
+  router.push({ name: "StudentDetailPage", params: { id: student.id } });
+};
 </script>
 
 <style scoped>
@@ -153,8 +150,4 @@ const filteredAlunos = computed(() => {
 .list-row:hover {
   background-color: #f5f5f5;
 }
-
-.row-selected {
-  background-color: #b2dfdb !important;
-}
 </style>

+ 0 - 0
src/pages/dashboard/components/FrequenciaMediaDialog.vue → src/pages/dashboard/components/AverageAttendanceDialog.vue


+ 0 - 0
src/pages/dashboard/components/ContratosCanceladosDialog.vue → src/pages/dashboard/components/CancelledContractsDialog.vue


+ 0 - 0
src/pages/dashboard/components/ContratosCongeladosDialog.vue → src/pages/dashboard/components/FrozenContractsDialog.vue


+ 0 - 0
src/pages/dashboard/components/FeriadosEditDialog.vue → src/pages/dashboard/components/HolidayEditDialog.vue


+ 2 - 2
src/pages/dashboard/components/FeriadosDialog.vue → src/pages/dashboard/components/HolidaysDialog.vue

@@ -178,7 +178,7 @@ import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue
 import DefaultInput from "src/components/defaults/DefaultInput.vue";
 import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
 import { createBaseHoliday } from "src/api/holiday";
-import FeriadosEditDialog from "src/pages/dashboard/components/FeriadosEditDialog.vue";
+import HolidayEditDialog from "src/pages/dashboard/components/HolidayEditDialog.vue";
 
 defineEmits([...useDialogPluginComponent.emits]);
 
@@ -329,7 +329,7 @@ async function saveNewRecord() {
 
 function openEdit(holiday) {
   $q.dialog({
-    component: FeriadosEditDialog,
+    component: HolidayEditDialog,
     componentProps: { holiday },
   }).onOk(({ action, holiday: updated, id }) => {
     if (action === "update") {

+ 0 - 0
src/pages/dashboard/components/TicketsAbertoDialog.vue → src/pages/dashboard/components/OpenTicketsDialog.vue


+ 0 - 0
src/pages/dashboard/components/EstoqueProdutosDialog.vue → src/pages/dashboard/components/ProductStockDialog.vue


+ 11 - 0
src/router/routes/student.route.js

@@ -0,0 +1,11 @@
+export default [
+  {
+    path: "/students/:id",
+    name: "StudentDetailPage",
+    component: () => import("pages/dashboard/StudentDetailPage.vue"),
+    meta: {
+      title: { value: "Student Detail", translate: false },
+      requireAuth: true,
+    },
+  },
+];