| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- <template>
- <div>
- <DefaultHeaderPage class="q-pa-sm">
- <template #after>
- <div class="flex items-center no-wrap" style="gap: 12px">
- <template v-if="$q.screen.gt.sm">
- <div
- class="column"
- style="line-height: 1.2; white-space: nowrap; flex-shrink: 0"
- >
- <span class="text-caption text-grey-6 text-primary text-center"
- >Ultimo acesso</span
- >
- <span class="text-caption text-primary text-center"
- >16/02/2026, 14:16</span
- >
- </div>
- </template>
- <div
- class="flex items-center no-wrap"
- style="gap: 2px; flex-shrink: 0"
- >
- <q-btn flat round dense icon="mdi-bell-badge" color="secondary" />
- <q-btn flat round dense icon="mdi-account" color="secondary" />
- <q-btn flat round dense icon="mdi-cog-outline" color="secondary" />
- </div>
- </div>
- </template>
- </DefaultHeaderPage>
- <div class="q-pa-sm">
- <div class="stat-cards-row q-mb-md">
- <DashboardStatCard
- title="Total alunos (contratos ativos)"
- icon="mdi-account-multiple-outline"
- value="0"
- badge="0 ativos"
- />
- <DashboardStatCard
- title="Receita Total"
- icon="mdi-currency-usd"
- value="R$ 0,00"
- subtitle="0 pagamentos pendentes"
- />
- <DashboardStatCard
- title="Ticket Médio"
- icon="mdi-calendar-blank"
- value="R$ 12,00"
- subtitle="Estável"
- />
- <DashboardStatCard
- title="Aniversariantes"
- icon="mdi-emoticon-happy-outline"
- value="0"
- subtitle="Fortaleça seus relacionamentos"
- />
- </div>
- <div class="row q-col-gutter-md q-mb-md">
- <div class="col-12 col-md-5">
- <q-card flat bordered style="height: 280px">
- <q-card-section class="row justify-between items-center q-pb-xs">
- <span class="text-subtitle2 text-weight-medium"
- >Faturamento Serviço / Materiais</span
- >
- <q-icon name="mdi-book-open-outline" color="grey-5" />
- </q-card-section>
- <q-separator />
- <q-card-section
- style="height: calc(100% - 57px)"
- class="q-pt-sm q-px-sm"
- >
- <Bar :data="faturamentoData" :options="faturamentoOptions" />
- </q-card-section>
- </q-card>
- </div>
- <div class="col-12 col-md-4">
- <q-card flat bordered style="height: 280px">
- <q-card-section class="row justify-between items-center q-pb-xs">
- <span class="text-subtitle2 text-weight-medium"
- >Contratos Ativos</span
- >
- <q-icon name="mdi-trending-up" color="grey-5" />
- </q-card-section>
- <q-separator />
- <q-card-section
- class="flex flex-center q-pt-sm"
- style="height: calc(100% - 57px); position: relative"
- >
- <div style="height: 100%; max-width: 280px; width: 100%">
- <Doughnut
- :data="gaugeData"
- :options="gaugeOptions"
- :plugins="[gaugeNeedlePlugin]"
- />
- </div>
- <div class="gauge-label">
- <div class="text-h5 text-bold">70</div>
- <div class="text-caption text-grey-6">Grade</div>
- </div>
- </q-card-section>
- </q-card>
- </div>
- <div class="col-12 col-md-3">
- <q-card flat bordered style="height: 280px">
- <q-card-section class="row justify-between items-center q-pb-xs">
- <span class="text-subtitle2 text-weight-medium"
- >Atalhos rápidos</span
- >
- <q-icon name="mdi-apps" color="grey-5" />
- </q-card-section>
- <q-separator />
- <q-card-section class="q-pt-md column q-gutter-sm">
- <q-btn
- unelevated
- color="primary"
- label="Criar contrato"
- no-caps
- class="full-width"
- />
- <q-btn
- unelevated
- color="primary"
- label="Registrar presença"
- no-caps
- class="full-width"
- />
- <q-btn
- unelevated
- color="primary"
- label="Novo pedido"
- no-caps
- class="full-width"
- />
- </q-card-section>
- </q-card>
- </div>
- </div>
- <!-- Row 3: Bottom -->
- <div class="row q-col-gutter-md">
- <div class="col-12 col-md-4">
- <q-card flat bordered style="height: 320px">
- <q-card-section class="row justify-between items-center q-pb-xs">
- <span class="text-subtitle2 text-weight-medium"
- >Matrículas por Período</span
- >
- <q-icon name="mdi-book-open-outline" color="grey-5" />
- </q-card-section>
- <q-separator />
- <q-card-section
- style="height: calc(100% - 57px)"
- class="q-pt-sm q-px-sm"
- >
- <Bar
- :data="matriculasData"
- :options="matriculasOptions"
- :plugins="[ChartDataLabels]"
- />
- </q-card-section>
- </q-card>
- </div>
- <div class="col-12 col-md-4">
- <q-card flat bordered style="height: 320px">
- <q-card-section class="row justify-between items-center q-pb-xs">
- <span class="text-subtitle2 text-weight-medium"
- >Aniversariantes do Mês</span
- >
- <q-icon name="mdi-gift-outline" color="grey-5" />
- </q-card-section>
- <q-separator />
- <div class="row justify-between items-center q-px-md q-py-xs">
- <span class="text-caption text-grey-6">Nome</span>
- <span class="text-caption text-grey-6">Ações</span>
- </div>
- <q-separator />
- <div style="height: calc(100% - 105px); overflow-y: auto">
- <q-list separator>
- <q-item
- v-for="(pessoa, i) in aniversariantes"
- :key="i"
- dense
- class="q-py-sm"
- >
- <q-item-section avatar>
- <q-avatar
- :color="pessoa.color"
- text-color="white"
- size="34px"
- class="text-caption text-bold"
- >
- {{ pessoa.nome[0] }}
- </q-avatar>
- </q-item-section>
- <q-item-section>
- <q-item-label>{{ pessoa.nome }}</q-item-label>
- </q-item-section>
- <q-item-section side>
- <div class="row">
- <q-btn
- flat
- dense
- round
- icon="mdi-whatsapp"
- color="green"
- size="sm"
- />
- <q-btn
- flat
- dense
- round
- icon="mdi-email-outline"
- color="grey-6"
- size="sm"
- />
- </div>
- </q-item-section>
- </q-item>
- </q-list>
- </div>
- </q-card>
- </div>
- <div class="col-12 col-md-4">
- <q-card flat bordered style="height: 320px">
- <q-card-section class="row justify-between items-center q-pb-xs">
- <span class="text-subtitle2 text-weight-medium"
- >Feriados do mês</span
- >
- <q-icon name="mdi-calendar-outline" color="grey-5" />
- </q-card-section>
- <q-separator />
- <q-card-section class="q-pt-md">
- <q-btn
- unelevated
- color="primary"
- label="Nova data"
- no-caps
- class="full-width q-mb-md"
- />
- <div class="row q-gutter-sm">
- <div
- v-for="(feriado, i) in feriados"
- :key="i"
- class="column items-center"
- style="min-width: 52px"
- >
- <q-badge
- :color="feriado.color"
- class="text-subtitle1 text-bold q-pa-sm"
- style="min-width: 40px; justify-content: center"
- >
- {{ feriado.dia }}
- </q-badge>
- <div class="text-caption q-mt-xs text-center">
- {{ feriado.nome }}
- </div>
- </div>
- </div>
- </q-card-section>
- </q-card>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref } from "vue";
- import { Bar, Doughnut } from "vue-chartjs";
- import {
- Chart as ChartJS,
- Title,
- Tooltip,
- Legend,
- BarElement,
- CategoryScale,
- LinearScale,
- ArcElement,
- } from "chart.js";
- import ChartDataLabels from "chartjs-plugin-datalabels";
- import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
- import DashboardStatCard from "src/components/charts/DashboardStatCard.vue";
- ChartJS.register(
- Title,
- Tooltip,
- Legend,
- BarElement,
- CategoryScale,
- LinearScale,
- ArcElement,
- );
- const faturamentoData = ref({
- labels: [
- "17/02",
- "18/02",
- "19/02",
- "20/02",
- "21/02",
- "22/02",
- "23/02",
- "24/02",
- "25/02",
- "26/02",
- "27/02",
- "28/02",
- ],
- datasets: [
- {
- label: "Serviços",
- data: [120, 185, 95, 210, 155, 200, 170, 130, 195, 160, 145, 180],
- backgroundColor: "rgba(99, 102, 241, 0.75)",
- borderColor: "rgba(99, 102, 241, 1)",
- borderWidth: 1,
- },
- {
- label: "Materiais",
- data: [75, 115, 60, 140, 95, 125, 105, 85, 135, 100, 90, 115],
- backgroundColor: "rgba(236, 72, 153, 0.75)",
- borderColor: "rgba(236, 72, 153, 1)",
- borderWidth: 1,
- },
- ],
- });
- const faturamentoOptions = ref({
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: { display: true, position: "top", labels: { font: { size: 11 } } },
- tooltip: { mode: "index", intersect: false },
- datalabels: { display: false },
- },
- scales: {
- x: { ticks: { font: { size: 9 } }, grid: { display: false } },
- y: { ticks: { font: { size: 10 } }, suggestedMin: 0 },
- },
- });
- const gaugeData = ref({
- datasets: [
- {
- backgroundColor: [
- "#00a550",
- "#4dbb7e",
- "#9ad2ad",
- "#cce156",
- "#fff100",
- "#ffbe00",
- "#ff8c00",
- "#FC3D23",
- "#D01616",
- "#8A0000",
- ],
- data: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
- needleValue: 7,
- borderColor: "transparent",
- },
- ],
- });
- const gaugeOptions = ref({
- rotation: 270,
- circumference: 180,
- cutout: "55%",
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- tooltip: { enabled: false },
- legend: { display: false },
- datalabels: { display: false },
- },
- });
- const gaugeNeedlePlugin = {
- id: "gaugeNeedle",
- afterDatasetsDraw(chart) {
- const { ctx, data } = chart;
- ctx.save();
- const needleValue = data.datasets[0].needleValue;
- const meta = chart.getDatasetMeta(0).data[0];
- const xCenter = meta.x;
- const yCenter = meta.y;
- const outerRadius = meta.outerRadius - 20;
- const circumference =
- (meta.circumference / Math.PI / data.datasets[0].data[0]) * needleValue;
- const angle = Math.PI;
- ctx.translate(xCenter, yCenter);
- ctx.rotate(angle * (circumference + 1.5));
- ctx.beginPath();
- ctx.strokeStyle = "grey";
- ctx.fillStyle = "grey";
- ctx.moveTo(-3, 0);
- ctx.lineTo(0, -outerRadius);
- ctx.lineTo(3, 0);
- ctx.stroke();
- ctx.fill();
- ctx.beginPath();
- ctx.arc(0, 0, 6, 0, 2 * Math.PI);
- ctx.fillStyle = "grey";
- ctx.fill();
- ctx.restore();
- },
- };
- const matriculasData = ref({
- labels: ["JAN", "FEV", "MAR", "ABR", "MAI", "JUN"],
- datasets: [
- {
- label: "Matrículas",
- data: [120, 200, 150, 80, 70, 110],
- backgroundColor: [
- "#2196F3",
- "#F44336",
- "#9C27B0",
- "#FFC107",
- "#212121",
- "#009688",
- ],
- borderColor: [
- "#2196F3",
- "#F44336",
- "#9C27B0",
- "#FFC107",
- "#212121",
- "#009688",
- ],
- borderWidth: 1,
- borderRadius: 2,
- },
- ],
- });
- const matriculasOptions = ref({
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: { display: false },
- datalabels: {
- anchor: "end",
- align: "top",
- color: "#666",
- font: { size: 11, weight: "bold" },
- formatter: (v) => v,
- },
- },
- scales: {
- x: { ticks: { font: { size: 11 } }, grid: { display: false } },
- y: { display: false, suggestedMin: 0, suggestedMax: 230 },
- },
- });
- const aniversariantes = ref([
- { nome: "Heloisa Faria", color: "orange" },
- { nome: "Juliana Costa", color: "green" },
- { nome: "Juliana Costa", color: "green" },
- { nome: "Fernando Almeida", color: "blue" },
- { nome: "Lucas Pereira", color: "purple" },
- { nome: "Sofia Martins", color: "teal" },
- ]);
- const feriados = ref([
- { dia: 17, nome: "Carnaval", color: "amber" },
- { dia: 17, nome: "Carnaval", color: "amber" },
- { dia: 17, nome: "Carnaval", color: "amber" },
- ]);
- </script>
- <style scoped>
- .stat-cards-row {
- display: flex;
- flex-wrap: nowrap;
- gap: 16px;
- }
- .stat-cards-row > * {
- flex: 1 1 0;
- min-width: 0;
- }
- @media (max-width: 599px) {
- .stat-cards-row {
- flex-wrap: wrap;
- }
- .stat-cards-row > * {
- flex: 1 1 calc(50% - 8px);
- }
- }
- .gauge-label {
- position: absolute;
- bottom: 28%;
- left: 50%;
- transform: translateX(-50%);
- text-align: center;
- pointer-events: none;
- }
- </style>
|