feat: implementa dashboard de monitoramento de servidores
- Adiciona biblioteca lucide-vue-next para ícones modernos - Cria componente ServerCard com informações detalhadas: * Exibe nome, IP e status do servidor * Barras de progresso para RAM e armazenamento * Cores dinâmicas baseadas na porcentagem de uso * Informações detalhadas de memória (livre, cache) - Aprimora SidePanel com funcionalidade de colapsar: * Botão para expandir/recolher o painel lateral * Ícones lucide para navegação (Dashboard, Settings) * Estados ativos baseados na rota atual - Implementa dados mock para 4 servidores de exemplo - Atualiza Dashboard com layout em grid dos cards de servidormaster
parent
eec21b7118
commit
f80f277660
|
@ -9,6 +9,7 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"lucide-vue-next": "^0.539.0",
|
||||
"vue": "^3.5.18",
|
||||
"vue-router": "4"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<script setup lang="ts">
|
||||
import { Server } from 'lucide-vue-next';
|
||||
import { computed } from 'vue';
|
||||
import type { ServerData } from '../mocks/mockServersList';
|
||||
|
||||
const props = defineProps<ServerData>();
|
||||
|
||||
// Função para calcular a porcentagem de uso do armazenamento
|
||||
const storagePercentage = computed(() => {
|
||||
// Função para converter para GB
|
||||
const convertToGB = (value: string): number => {
|
||||
const num = parseFloat(value.replace(/[^\d.]/g, ''));
|
||||
if (value.includes('TB')) {
|
||||
return num * 1024; // Converte TB para GB
|
||||
}
|
||||
return num; // Já está em GB
|
||||
};
|
||||
|
||||
const totalGB = convertToGB(props.totalStorage);
|
||||
const usedGB = convertToGB(props.usedStorage);
|
||||
|
||||
return Math.round((usedGB / totalGB) * 100);
|
||||
});
|
||||
|
||||
// Função para calcular a porcentagem de uso da RAM
|
||||
const ramPercentage = computed(() => {
|
||||
const total = parseFloat(props.totalRam.replace(/[^\d.]/g, ''));
|
||||
const used = parseFloat(props.totalRamUsed.replace(/[^\d.]/g, ''));
|
||||
|
||||
return Math.round((used / total) * 100);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-cardBg text-text rounded-lg p-4">
|
||||
<div class="flex flex-row items-center gap-4">
|
||||
<div class="p-2 bg-bg rounded-md">
|
||||
<Server :size="32" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-xl font-bold">{{ props.name }}</h1>
|
||||
<h1 class="text-green-600">{{ props.ip }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-1 bg-bg my-4" />
|
||||
|
||||
<div>
|
||||
<div class="flex flex-row justify-between text-lg">
|
||||
<h1 class="font-bold">Ram total: </h1>
|
||||
<h1>{{ props.totalRam }}</h1>
|
||||
</div>
|
||||
|
||||
<!-- Barra de progresso da RAM (menor) -->
|
||||
<div class="mt-1 mb-2">
|
||||
<div class="flex justify-between text-md text-gray-400 mb-1">
|
||||
<span>{{ props.totalRamUsed }}</span>
|
||||
<span>{{ ramPercentage }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-bg rounded-full h-1.5">
|
||||
<div class="h-1.5 rounded-full transition-all duration-300"
|
||||
:class="ramPercentage > 80 ? 'bg-red-400' : ramPercentage > 60 ? 'bg-yellow-400' : 'bg-blue-400'"
|
||||
:style="`width: ${ramPercentage}%`"></div>
|
||||
</div>
|
||||
<!-- Informações detalhadas da RAM -->
|
||||
<div class="flex justify-between text-md text-gray-400 mt-1">
|
||||
<span>Livre: {{ props.freeRam }}</span>
|
||||
<span>Cache: {{ props.cachedRam }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row justify-between text-lg">
|
||||
<h1 class="font-bold">Armazenamento: </h1>
|
||||
<h1>{{ props.totalStorage }}</h1>
|
||||
</div>
|
||||
|
||||
<!-- Barra de progresso do armazenamento -->
|
||||
<div class="mt-2 mb-3">
|
||||
<div class="flex justify-between text-sm text-gray-400 mb-1">
|
||||
<span>Usado: {{ props.usedStorage }}</span>
|
||||
<span>{{ storagePercentage }}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-bg rounded-full h-2">
|
||||
<div class="h-2 rounded-full transition-all duration-300"
|
||||
:class="storagePercentage > 80 ? 'bg-red-500' : storagePercentage > 60 ? 'bg-yellow-500' : 'bg-green-500'"
|
||||
:style="`width: ${storagePercentage}%`"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,22 +1,53 @@
|
|||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import NavButton from './NavButton.vue';
|
||||
import { ChevronLeft, ChevronRight, LayoutDashboard, Settings } from 'lucide-vue-next';
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const route = useRouter();
|
||||
const route = useRoute();
|
||||
const isCollapsed = ref(false);
|
||||
|
||||
const toggleCollapse = () => {
|
||||
isCollapsed.value = !isCollapsed.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-screen bg-cardBg w-fit p-10 border-r-2 border-border flex flex-col gap-10">
|
||||
<h1 class="text-accent font-bold text-3xl">Omnihit Manager</h1>
|
||||
<div class="flex flex-col gap-3">
|
||||
<RouterLink to="/">
|
||||
<NavButton title="painel" :isActive="true" />
|
||||
</RouterLink>
|
||||
<RouterLink to="/sections">
|
||||
<NavButton title="Seções" :isActive="true" />
|
||||
</RouterLink>
|
||||
|
||||
<div class="h-screen bg-cardBg border-r-2 border-border flex flex-col gap-10 transition-all duration-300"
|
||||
:class="isCollapsed ? 'w-20 py-10 px-4' : 'w-fit p-10'">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-accent font-bold text-3xl transition-opacity duration-300"
|
||||
:class="isCollapsed ? 'opacity-0 hidden' : 'opacity-100'">
|
||||
Omnihit Manager
|
||||
</h1>
|
||||
<button @click="toggleCollapse"
|
||||
class="p-2 rounded-lg hover:bg-bg transition-colors duration-200 text-accent">
|
||||
<ChevronLeft v-if="!isCollapsed" :size="24" />
|
||||
<ChevronRight v-else :size="24" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3">
|
||||
<RouterLink to="/" class="block">
|
||||
<div class="flex items-center gap-3 text-2xl bg-bg p-3 w-full rounded-lg border-2 border-border hover:bg-cardHover transition duration-150 cursor-pointer"
|
||||
:class="route.path === '/' ? 'text-accent border-accent' : 'text-text'">
|
||||
<LayoutDashboard :size="24" class="flex-shrink-0" />
|
||||
<span class="capitalize font-bold transition-opacity duration-300"
|
||||
:class="isCollapsed ? 'opacity-0 hidden' : 'opacity-100'">
|
||||
Painel
|
||||
</span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
|
||||
<RouterLink to="/sections" class="block">
|
||||
<div class="flex items-center gap-3 text-2xl bg-bg p-3 w-full rounded-lg border-2 border-border hover:bg-cardHover transition duration-150 cursor-pointer"
|
||||
:class="route.path === '/sections' ? 'text-accent border-accent' : 'text-text'">
|
||||
<Settings :size="24" class="flex-shrink-0" />
|
||||
<span class="capitalize font-bold transition-opacity duration-300"
|
||||
:class="isCollapsed ? 'opacity-0 hidden' : 'opacity-100'">
|
||||
Seções
|
||||
</span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,58 @@
|
|||
export interface ServerData {
|
||||
name: string;
|
||||
ip: string;
|
||||
totalRam: string;
|
||||
totalRamUsed: string;
|
||||
cachedRam: string;
|
||||
freeRam: string;
|
||||
totalStorage: string;
|
||||
usedStorage: string;
|
||||
freeStorage: string;
|
||||
}
|
||||
|
||||
export const mockServersList: ServerData[] = [
|
||||
{
|
||||
name: "Servidor Web 01",
|
||||
ip: "192.168.1.10",
|
||||
totalRam: "16GB",
|
||||
totalRamUsed: "8.5GB",
|
||||
cachedRam: "2.1GB",
|
||||
freeRam: "7.5GB",
|
||||
totalStorage: "500GB",
|
||||
usedStorage: "320GB",
|
||||
freeStorage: "180GB"
|
||||
},
|
||||
{
|
||||
name: "Servidor BD Principal",
|
||||
ip: "192.168.1.11",
|
||||
totalRam: "32GB",
|
||||
totalRamUsed: "24.2GB",
|
||||
cachedRam: "4.8GB",
|
||||
freeRam: "7.8GB",
|
||||
totalStorage: "1TB",
|
||||
usedStorage: "750GB",
|
||||
freeStorage: "250GB"
|
||||
},
|
||||
{
|
||||
name: "Servidor API",
|
||||
ip: "192.168.1.12",
|
||||
totalRam: "8GB",
|
||||
totalRamUsed: "3.2GB",
|
||||
cachedRam: "1.1GB",
|
||||
freeRam: "4.8GB",
|
||||
totalStorage: "250GB",
|
||||
usedStorage: "120GB",
|
||||
freeStorage: "130GB"
|
||||
},
|
||||
{
|
||||
name: "Servidor Backup",
|
||||
ip: "192.168.1.13",
|
||||
totalRam: "16GB",
|
||||
totalRamUsed: "5.1GB",
|
||||
cachedRam: "2.5GB",
|
||||
freeRam: "10.9GB",
|
||||
totalStorage: "2TB",
|
||||
usedStorage: "1.2TB",
|
||||
freeStorage: "800GB"
|
||||
}
|
||||
];
|
|
@ -1,8 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import ServerCard from '../components/ServerCard.vue';
|
||||
import { mockServersList } from '../mocks/mockServersList';
|
||||
|
||||
const servers = mockServersList;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-bg h-screen text-text">
|
||||
<div class="bg-bg h-screen text-text p-10">
|
||||
<div class="grid grid-cols-4 gap-6">
|
||||
<ServerCard v-for="server in servers" :key="server.ip" v-bind="server" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -871,6 +871,11 @@ lru-cache@^10.2.0:
|
|||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
|
||||
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
|
||||
|
||||
lucide-vue-next@^0.539.0:
|
||||
version "0.539.0"
|
||||
resolved "https://registry.yarnpkg.com/lucide-vue-next/-/lucide-vue-next-0.539.0.tgz#e10c39ab86f08f9fee9180df9c3c8e89c65632ee"
|
||||
integrity sha512-8Y75ekxsBqW+9YZPCbxE6KXoCbNmJYUujKP+nK2cIqmONJXvUSeyroEW4DV1Kjlw8ZvmfKwP0FpdjPzuKvRsQw==
|
||||
|
||||
magic-string@^0.30.17:
|
||||
version "0.30.17"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
|
||||
|
|
Loading…
Reference in New Issue