feat(ui): adicionar suporte avançado de tema

- define tokens CSS/Tailwind com variantes claras e escuras
- adapta layout, filtros, modais e tabela aos novos estilos
- adiciona favicon otimizado e controles de dados sensíveis
master
Artur Oliveira 2025-12-16 18:57:11 -03:00
parent c665aa18ea
commit 3104f83170
17 changed files with 174 additions and 112 deletions

View File

@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/png" href="/favicon.png" sizes="96x96" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>frontned</title>
<title>Hit Servers Manager</title>
</head>
<body>
<div id="root"></div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1,3 +1,31 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind utilities;
:root {
--color-bg: 250 250 249;
--color-card: 244 244 242;
--color-card-border: 229 231 235;
--color-text: 26 26 26;
--color-text-secondary: 107 114 128;
--color-hover: 208 74 15;
--color-accent: 233 90 27;
color-scheme: light;
}
.dark {
--color-bg: 2 6 23;
--color-card: 15 23 42;
--color-card-border: 51 65 85;
--color-text: 241 245 249;
--color-text-secondary: 148 163 184;
--color-hover: 249 115 22;
--color-accent: 233 90 27;
color-scheme: dark;
}
@layer base {
body {
@apply bg-bg text-text antialiased transition-colors duration-200;
}
}

View File

@ -5,18 +5,19 @@ import { Dashboard } from './pages/Dashboard';
import { ProtectedRoute } from './routes/ProtectedRoute';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
<Route path="/login" element={<Login />} />
</Routes>
</BrowserRouter>
<div className="min-h-screen bg-bg text-text transition-colors duration-200 dark:bg-slate-950 dark:text-slate-100">
<BrowserRouter>
<Routes>
<Route path="/" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
<Route path="/login" element={<Login />} />
</Routes>
</BrowserRouter>
</div>
);
}

View File

@ -254,5 +254,5 @@ export const Header = ({
};
const Styles = {
wrapper: "flex items-center justify-between rounded-xl border border-cardBorder bg-card px-6 py-4 shadow-sm",
wrapper: "flex items-center justify-between rounded-xl border border-cardBorder bg-card px-6 py-4 shadow-sm dark:border-slate-800 dark:bg-slate-900 dark:shadow-slate-900/50",
};

View File

@ -74,11 +74,11 @@ export const ServerCardMetrics = () => {
const Styles = {
grid: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
card: "flex items-center gap-4 rounded-xl border border-cardBorder bg-gradient-to-br from-white/90 to-card p-5 shadow-sm",
iconWrapper: "flex h-14 w-14 items-center justify-center rounded-lg border border-accent/20 bg-accent/10 text-accent",
card: "flex items-center gap-4 rounded-xl border border-cardBorder bg-gradient-to-br from-white/90 to-card p-5 shadow-sm dark:border-slate-800 dark:from-slate-900 dark:to-slate-950 dark:shadow-slate-900/40",
iconWrapper: "flex h-14 w-14 items-center justify-center rounded-lg border border-accent/20 bg-accent/10 text-accent dark:border-slate-800 dark:bg-slate-800/60",
textGroup: "flex flex-col",
label: "text-xs font-medium uppercase tracking-wide text-text-secondary",
value: "text-3xl font-semibold text-text leading-tight",
placeholder: "p-4 rounded-lg border border-cardBorder bg-card text-text-secondary text-sm",
error: "p-4 rounded-lg border border-red-200 bg-red-50 text-red-600 text-sm",
label: "text-xs font-medium uppercase tracking-wide text-text-secondary dark:text-slate-400",
value: "text-3xl font-semibold leading-tight text-text dark:text-white",
placeholder: "rounded-lg border border-cardBorder bg-card p-4 text-sm text-text-secondary dark:border-slate-800 dark:bg-slate-900 dark:text-slate-400",
error: "rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-600 dark:border-red-400/30 dark:bg-red-950/40 dark:text-red-300",
};

View File

@ -15,6 +15,8 @@ interface Props {
serverTypeOptions?: ServersType[];
applicationOptions?: Applications[];
databaseOptions?: DatabaseType[];
hideSensitive: boolean;
onToggleSensitive: () => void;
}
const withAllOption = <T extends string>(options?: T[]): Array<T | OptionAll> => {
@ -37,6 +39,8 @@ export const ServersFilterBar = ({
serverTypeOptions,
applicationOptions,
databaseOptions,
hideSensitive,
onToggleSensitive,
}: Props) => {
const typeOptions = withAllOption(serverTypeOptions);
const applicationOptionsList = withAllOption(applicationOptions);
@ -73,6 +77,11 @@ export const ServersFilterBar = ({
onChange={(event) => onDbTypeChange(event.target.value as DatabaseType | OptionAll)}
options={databaseOptionsList}
/>
<div className={Styles.toggleWrapper}>
<button type="button" className={Styles.toggleButton} onClick={onToggleSensitive}>
{hideSensitive ? "Mostrar dados confidenciais" : "Ocultar dados confidenciais"}
</button>
</div>
</div>
);
};
@ -100,10 +109,12 @@ const Select = <T extends string>({ label, value, onChange, options }: SelectPro
};
const Styles = {
wrapper: "flex flex-wrap gap-4 rounded-lg border border-cardBorder bg-white/70 p-4 shadow-sm",
wrapper: "flex flex-wrap items-end gap-4 rounded-lg border border-cardBorder bg-white/70 p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900 dark:shadow-slate-900/40",
searchGroup: "flex-1 min-w-[220px]",
selectGroup: "flex min-w-[150px] flex-col gap-1",
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary",
input: "w-full rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent",
select: "w-full rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent capitalize",
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary dark:text-slate-400",
input: "w-full rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
select: "w-full rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent capitalize dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
toggleWrapper: "flex min-w-[200px] items-center justify-end",
toggleButton: "rounded-md border border-cardBorder px-4 py-2 text-xs font-semibold uppercase tracking-wide text-text-secondary hover:bg-bg dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800",
};

View File

@ -1,4 +1,3 @@
import { useState } from "react";
import type { Server } from "../types/Server";
interface Props {
@ -9,6 +8,7 @@ interface Props {
pageSize: number;
totalPages: number;
totalItems: number;
hideSensitive: boolean;
onPageChange: (page: number) => void;
onPageSizeChange: (size: number) => void;
}
@ -23,38 +23,36 @@ export const ServersTable = ({
pageSize,
totalPages,
totalItems,
hideSensitive,
onPageChange,
onPageSizeChange,
}: Props) => {
const [hideSensitive, setHideSensitive] = useState(false);
const showingFrom = totalItems === 0 ? 0 : page * pageSize + 1;
const showingTo = totalItems === 0 ? 0 : Math.min((page + 1) * pageSize, totalItems);
const hasResults = servers.length > 0;
const isInitialLoad = loading && !hasResults;
const showOverlay = loading && hasResults;
const formatSensitiveValue = (value: string | number) => (hideSensitive ? "••••" : value);
return (
<div className={Styles.card}>
<button
type="button"
className={Styles.hideButton}
onClick={() => setHideSensitive((prev) => !prev)}
disabled={loading || (!hasResults && !error)}
aria-pressed={hideSensitive}
>
{hideSensitive ? "Mostrar dados sensíveis" : "Ocultar dados sensíveis"}
</button>
{loading && <div className={Styles.status}>Carregando servidores...</div>}
{isInitialLoad && <div className={Styles.status}>Carregando servidores...</div>}
{error && <div className={Styles.error}>{error}</div>}
{!loading && !error && servers.length === 0 && (
<div className={Styles.status}>Nenhum servidor encontrado.</div>
)}
{!loading && !error && hasResults && (
<>
{hasResults && (
<div className="relative space-y-4">
{showOverlay && (
<div className={Styles.loadingOverlay}>
<span>Atualizando lista...</span>
</div>
)}
<div className={Styles.tableWrapper}>
<table className={Styles.table}>
<thead className={Styles.tableHead}>
<tr className="text-left">
<tr className={Styles.tableHeadRow}>
<th className={Styles.tableHeadCell}>Nome</th>
<th className={Styles.tableHeadCell}>IP</th>
<th className={Styles.tableHeadCell}>Porta</th>
@ -69,14 +67,14 @@ export const ServersTable = ({
{servers.map((server) => (
<tr
key={server.id}
className="hover:bg-gray-50 transition-colors even:bg-gray-50/50"
className={Styles.tableRow}
>
<td className={Styles.rowCell}>{server.name}</td>
<td className={Styles.rowCell}>{formatSensitiveValue(server.ip)}</td>
<td className={Styles.rowCell}>{formatSensitiveValue(server.port)}</td>
<td className={Styles.rowCell}>{formatSensitiveValue(server.user)}</td>
<td className={Styles.rowCell}>
<code className="text-xs bg-gray-100 px-2 py-1 rounded">
<code className={Styles.password}>
{hideSensitive ? "••••" : server.password}
</code>
</td>
@ -130,29 +128,34 @@ export const ServersTable = ({
</div>
</div>
</div>
</>
</div>
)}
<div className={Styles.footerSpacer} aria-hidden />
</div>
);
};
const Styles = {
card: "relative bg-card border border-cardBorder shadow-sm rounded-lg overflow-hidden",
status: "p-4 text-text-secondary text-sm",
error: "p-4 text-red-600 text-sm",
tableWrapper: "overflow-x-auto rounded-lg shadow-sm border border-cardBorder",
table: "min-w-full divide-y divide-cardBorder table-auto",
tableHead: "bg-gray-50 sticky top-0",
tableHeadCell: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-text-secondary",
tableBody: "bg-white divide-y divide-cardBorder",
rowCell: "px-4 py-3 text-sm text-text",
pagination: "flex flex-col gap-2 border-t border-cardBorder bg-white px-4 py-3 sm:flex-row sm:items-center sm:justify-between",
pageInfo: "text-sm text-text-secondary",
card: "overflow-hidden rounded-lg border border-cardBorder bg-card shadow-sm dark:border-slate-800 dark:bg-slate-900 dark:shadow-slate-900/40",
status: "p-4 text-sm text-text-secondary dark:text-slate-400",
error: "p-4 text-sm text-red-600 dark:text-red-400",
tableWrapper: "overflow-x-auto rounded-lg border border-cardBorder shadow-sm dark:border-slate-800 dark:bg-slate-950/40",
table: "min-w-full table-auto divide-y divide-cardBorder dark:divide-slate-800",
tableHead: "sticky top-0 bg-gray-50 dark:bg-slate-800",
tableHeadRow: "text-left text-text dark:text-slate-200",
tableHeadCell: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-text-secondary dark:text-slate-300",
tableBody: "divide-y divide-cardBorder bg-white dark:divide-slate-800 dark:bg-slate-900",
tableRow: "transition-colors even:bg-gray-50/50 hover:bg-gray-50 dark:even:bg-slate-900 dark:hover:bg-slate-800",
rowCell: "px-4 py-3 text-sm text-text dark:text-slate-100",
password: "rounded bg-gray-100 px-2 py-1 text-xs dark:bg-slate-800 dark:text-slate-100",
pagination: "flex flex-col gap-2 border-t border-cardBorder bg-white px-4 py-3 dark:border-slate-800 dark:bg-slate-900 sm:flex-row sm:items-center sm:justify-between",
pageInfo: "text-sm text-text-secondary dark:text-slate-400",
paginationControls: "flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-4",
pageSizeLabel: "text-sm text-text flex items-center gap-2",
pageSizeSelect: "rounded-md border border-cardBorder bg-white px-2 py-1 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent",
pageSizeLabel: "text-sm text-text dark:text-slate-100 flex items-center gap-2",
pageSizeSelect: "rounded-md border border-cardBorder bg-white px-2 py-1 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
pageButtons: "flex items-center gap-3",
pageButton: "rounded-md border border-cardBorder px-3 py-1.5 text-sm font-medium text-text hover:bg-bg disabled:opacity-50 disabled:hover:bg-transparent",
pageIndicator: "text-sm text-text-secondary",
hideButton: "absolute right-3 top-3 rounded-md border border-cardBorder px-3 py-1.5 text-xs font-semibold uppercase tracking-wide text-text-secondary hover:bg-bg disabled:opacity-50 disabled:hover:bg-transparent bg-white shadow-sm",
pageButton: "rounded-md border border-cardBorder px-3 py-1.5 text-sm font-medium text-text transition-colors hover:bg-bg disabled:opacity-50 disabled:hover:bg-transparent dark:border-slate-700 dark:text-slate-100 dark:hover:bg-slate-800",
pageIndicator: "text-sm text-text-secondary dark:text-slate-400",
footerSpacer: "h-4",
loadingOverlay: "absolute inset-0 z-10 flex items-center justify-center rounded-lg bg-white/70 text-sm font-medium text-text backdrop-blur-sm dark:bg-slate-900/80 dark:text-slate-100",
};

View File

@ -64,11 +64,11 @@ export const Modal = ({ isOpen, title, onClose, children, description, bodyClass
};
const Styles = {
overlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 backdrop-blur-sm px-4 !mt-0",
dialog: "w-full max-w-2xl rounded-2xl border border-cardBorder bg-card p-6 shadow-xl transform transition-all duration-200 animate-fade-up",
header: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder",
title: "text-lg font-semibold text-text",
closeButton: "text-2xl leading-none text-text-secondary hover:text-text",
description: "pt-3 text-sm text-text-secondary",
overlay: "fixed inset-0 z-40 flex items-center justify-center bg-black/40 px-4 backdrop-blur-sm !mt-0 dark:bg-black/60",
dialog: "w-full max-w-2xl transform animate-fade-up rounded-2xl border border-cardBorder bg-card p-6 shadow-xl transition-all duration-200 dark:border-slate-800 dark:bg-slate-900 dark:shadow-slate-900/70",
header: "flex items-start justify-between gap-4 border-b border-cardBorder pb-4 dark:border-slate-800",
title: "text-lg font-semibold text-text dark:text-white",
closeButton: "text-2xl leading-none text-text-secondary hover:text-text dark:text-slate-400 dark:hover:text-white",
description: "pt-3 text-sm text-text-secondary dark:text-slate-400",
body: "pt-4",
};

View File

@ -34,7 +34,7 @@ export const BulkImportModal = ({
<Modal isOpen={isOpen} title="Cadastro em massa" onClose={onClose} bodyClassName={Styles.modalBody}>
<div className={Styles.uploadCard}>
<label htmlFor="bulk-file" className={Styles.dropLabel}>
<span className="text-base font-medium text-text">Selecionar arquivo CSV</span>
<span className="text-base font-medium text-text dark:text-white">Selecionar arquivo CSV</span>
<span className="text-xs text-text-secondary">
Arraste e solte ou clique para procurar
</span>
@ -115,19 +115,19 @@ const Stat = ({
const Styles = {
modalBody: "space-y-5",
uploadCard: "rounded-xl border border-dashed border-cardBorder bg-white/70 p-6 shadow-inner space-y-4",
dropLabel: "flex flex-col gap-1 text-center",
dropzone: "flex flex-col items-center justify-center rounded-lg border border-cardBorder bg-bg px-4 py-6 text-center cursor-pointer hover:border-accent hover:bg-white transition-colors",
helperText: "text-xs text-text-secondary",
uploadCard: "space-y-4 rounded-xl border border-dashed border-cardBorder bg-white/70 p-6 shadow-inner dark:border-slate-800 dark:bg-slate-900/70",
dropLabel: "flex flex-col gap-1 text-center text-text dark:text-white",
dropzone: "flex cursor-pointer flex-col items-center justify-center rounded-lg border border-cardBorder bg-bg px-4 py-6 text-center transition-colors hover:border-accent hover:bg-white dark:border-slate-700 dark:bg-slate-900 dark:hover:bg-slate-800",
helperText: "text-xs text-text-secondary dark:text-slate-400",
actionsRow: "flex flex-wrap items-center gap-3",
secondaryButton: "rounded-md border border-cardBorder px-4 py-2 text-sm font-medium text-text hover:bg-bg",
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70",
errorText: "text-sm text-red-600",
resultCard: "rounded-xl border border-cardBorder bg-white/90 p-5 space-y-4",
secondaryButton: "rounded-md border border-cardBorder px-4 py-2 text-sm font-medium text-text hover:bg-bg dark:border-slate-700 dark:text-slate-100 dark:hover:bg-slate-800",
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
errorText: "text-sm text-red-600 dark:text-red-400",
resultCard: "space-y-4 rounded-xl border border-cardBorder bg-white/90 p-5 dark:border-slate-800 dark:bg-slate-900/80",
statsGrid: "flex flex-wrap gap-4",
failureList: "list-disc pl-5 space-y-1 text-sm text-text-secondary max-h-40 overflow-auto",
failureList: "max-h-40 list-disc space-y-1 overflow-auto pl-5 text-sm text-text-secondary dark:text-slate-400",
statBase: "flex flex-col rounded-lg border px-4 py-3 text-sm",
statDefault: "border-cardBorder bg-white text-text",
statAccent: "border-accent/40 bg-accent/10 text-accent",
statDanger: "border-red-200 bg-red-50 text-red-600",
statDefault: "border-cardBorder bg-white text-text dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
statAccent: "border-accent/40 bg-accent/10 text-accent dark:border-accent/20 dark:bg-accent/20 dark:text-white",
statDanger: "border-red-200 bg-red-50 text-red-600 dark:border-red-400/40 dark:bg-red-500/10 dark:text-red-300",
};

View File

@ -50,8 +50,8 @@ export const HeaderActions = ({
const Styles = {
actions: "flex items-center gap-3",
menuTrigger: "rounded-lg border border-cardBorder bg-white/70 px-4 py-2 text-sm font-medium text-text flex items-center gap-2 hover:bg-white transition-colors",
dropdown: "absolute right-0 mt-2 w-48 rounded-lg border border-cardBorder bg-white py-2 shadow-lg z-10",
dropdownItem: "w-full px-4 py-2 text-left text-sm text-text-secondary hover:bg-bg hover:text-text transition-colors",
logoutButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-hover",
menuTrigger: "flex items-center gap-2 rounded-lg border border-cardBorder bg-white/70 px-4 py-2 text-sm font-medium text-text transition-colors hover:bg-white dark:border-slate-700 dark:bg-slate-800/80 dark:text-slate-100 dark:hover:bg-slate-800",
dropdown: "absolute right-0 z-10 mt-2 w-48 rounded-lg border border-cardBorder bg-white py-2 shadow-lg dark:border-slate-700 dark:bg-slate-900 dark:shadow-black/40",
dropdownItem: "w-full px-4 py-2 text-left text-sm text-text-secondary transition-colors hover:bg-bg hover:text-text dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white",
logoutButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-hover dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
};

View File

@ -13,6 +13,6 @@ export const HeaderBrand = () => {
const Styles = {
brand: "flex items-center gap-3",
logo: "h-10 w-10 object-contain",
title: "text-base font-semibold text-text",
subtitle: "text-xs uppercase tracking-wide text-text-secondary",
title: "text-base font-semibold text-text dark:text-white",
subtitle: "text-xs uppercase tracking-wide text-text-secondary dark:text-slate-400",
};

View File

@ -65,14 +65,14 @@ export const ProfileModal = ({
const Styles = {
modalBody: "space-y-4",
helperText: "text-sm text-text-secondary",
errorMessage: "text-sm text-red-600",
helperText: "text-sm text-text-secondary dark:text-slate-400",
errorMessage: "text-sm text-red-600 dark:text-red-400",
form: "space-y-4",
formGrid: "grid gap-4 md:grid-cols-2",
field: "flex flex-col gap-2",
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary",
input: "rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent disabled:opacity-70",
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary dark:text-slate-400",
input: "rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent disabled:opacity-70 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
modalActions: "flex justify-end gap-3 pt-2",
secondaryButton: "rounded-md border border-cardBorder px-4 py-2 text-sm font-medium text-text hover:bg-bg disabled:opacity-50",
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70",
secondaryButton: "rounded-md border border-cardBorder px-4 py-2 text-sm font-medium text-text hover:bg-bg disabled:opacity-50 dark:border-slate-700 dark:text-slate-100 dark:hover:bg-slate-800",
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
};

View File

@ -95,10 +95,10 @@ const Styles = {
form: "space-y-4",
formGrid: "grid gap-4 md:grid-cols-2",
field: "flex flex-col gap-2",
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary",
input: "rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent",
select: "rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent capitalize",
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary dark:text-slate-400",
input: "rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
select: "rounded-lg border border-cardBorder bg-white px-3 py-2 text-sm text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent capitalize dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
modalActions: "flex justify-end gap-3 pt-2",
secondaryButton: "rounded-md border border-cardBorder px-4 py-2 text-sm font-medium text-text hover:bg-bg disabled:opacity-50",
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70",
secondaryButton: "rounded-md border border-cardBorder px-4 py-2 text-sm font-medium text-text hover:bg-bg disabled:opacity-50 dark:border-slate-700 dark:text-slate-100 dark:hover:bg-slate-800",
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
};

View File

@ -26,6 +26,7 @@ export const Dashboard = () => {
const [pageSize, setPageSize] = useState<number>(10);
const [totalPages, setTotalPages] = useState<number>(0);
const [totalItems, setTotalItems] = useState<number>(0);
const [hideSensitive, setHideSensitive] = useState<boolean>(true);
const [serverTypeOptions, setServerTypeOptions] = useState<string[]>([]);
const [applicationOptions, setApplicationOptions] = useState<string[]>([]);
const [databaseOptions, setDatabaseOptions] = useState<string[]>([]);
@ -150,12 +151,16 @@ export const Dashboard = () => {
setPageSize(nextSize);
};
const handleToggleSensitive = () => {
setHideSensitive((prev) => !prev);
};
useEffect(() => {
fetchTypeOptions();
}, [fetchTypeOptions]);
return (
<Layout className="h-screen py-10">
<Layout className="h-full py-10">
<div className="space-y-6">
<Header
currentUser={currentUser}
@ -176,6 +181,8 @@ export const Dashboard = () => {
onTypeChange={handleTypeChange}
onApplicationChange={handleApplicationChange}
onDbTypeChange={handleDbTypeChange}
hideSensitive={hideSensitive}
onToggleSensitive={handleToggleSensitive}
serverTypeOptions={serverTypeOptions}
applicationOptions={applicationOptions}
databaseOptions={databaseOptions}
@ -188,6 +195,7 @@ export const Dashboard = () => {
pageSize={pageSize}
totalPages={totalPages}
totalItems={totalItems}
hideSensitive={hideSensitive}
onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange}
/>

View File

@ -50,11 +50,11 @@ export const Login = () => {
return (
<Layout className={Styles.layout}>
<img src="/logo.webp " alt="Logo" className="w-36 h-36 mx-auto mb-4" />
<img src="/logo.webp" alt="Logo" className="mx-auto mb-4 h-36 w-36" />
<div className={Styles.card}>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email" className="block mb-2 text-md font-medium text-text">Email:</label>
<label htmlFor="email" className={Styles.label}>Email:</label>
<input
type="email"
id="email"
@ -65,7 +65,7 @@ export const Login = () => {
onChange={handleChange}/>
</div>
<div className="relative">
<label htmlFor="password" className="block mb-2 text-md font-medium text-text">Password:</label>
<label htmlFor="password" className={Styles.label}>Password:</label>
<input
type={showPassword ? "text" : "password"}
id="password"
@ -84,7 +84,7 @@ export const Login = () => {
{showPassword ? <Eye size={18} /> : <EyeOff size={18} />}
</button>
</div>
{error && <p className="text-red-500 text-sm mt-2">{error}</p>}
{error && <p className="mt-2 text-sm text-red-500 dark:text-red-400">{error}</p>}
<button type="submit" disabled={loading} className={Styles.button}>
{loading ? "Autenticando..." : "Login"}
</button>
@ -95,9 +95,10 @@ export const Login = () => {
};
const Styles = {
layout: "h-screen flex flex-col gap-4 justify-center animate-fade-up",
card: "w-96 p-8 shadow-lg rounded-lg border border-cardBorder bg-card",
input: "bg-gray-50 border border-cardBorder text-text text-md rounded-lg outline-none block w-full p-2.5",
iconButton: "absolute right-3 top-1/2 text-text p-1 focus:outline-none",
button: "w-full bg-accent p-2 rounded-md mt-4 text-white font-bold text-lg hover:bg-hover transition duration-150",
layout: "flex h-screen animate-fade-up flex-col justify-center gap-4 px-4",
card: "w-full max-w-md rounded-lg border border-cardBorder bg-card p-8 shadow-lg dark:border-slate-800 dark:bg-slate-900 dark:shadow-slate-900/60",
label: "mb-2 block text-md font-medium text-text dark:text-slate-100",
input: "block w-full rounded-lg border border-cardBorder bg-gray-50 p-2.5 text-md text-text outline-none focus:border-accent focus:ring-1 focus:ring-accent dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
iconButton: "absolute right-3 top-9 p-1 text-text focus:outline-none dark:text-slate-200",
button: "mt-4 w-full rounded-md bg-accent p-2 text-lg font-bold text-white transition duration-150 hover:bg-hover dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
};

View File

@ -1,5 +1,15 @@
const withOpacity = (variable) => {
return ({ opacityValue } = {}) => {
if (opacityValue !== undefined) {
return `rgb(var(${variable}) / ${opacityValue})`;
}
return `rgb(var(${variable}))`;
};
};
/** @type {import('tailwindcss').Config} */
export default {
darkMode: "class",
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
@ -7,13 +17,13 @@ export default {
theme: {
extend: {
colors: {
bg: '#FAFAF9',
card: '#F4F4F2',
cardBorder: '#E5E7EB',
text: '#1A1A1A',
'text-secondary': '#6B7280',
hover: '#D04A0F',
accent: '#E95A1B',
bg: withOpacity("--color-bg"),
card: withOpacity("--color-card"),
cardBorder: withOpacity("--color-card-border"),
text: withOpacity("--color-text"),
"text-secondary": withOpacity("--color-text-secondary"),
hover: withOpacity("--color-hover"),
accent: withOpacity("--color-accent"),
},
keyframes: {
'fade-up': {