Compare commits
No commits in common. "7be806a81ec43f2b9ab9c5970df142a5cfd6fe6f" and "c665aa18ea70e5da1ea43b728921fbc108709efe" have entirely different histories.
7be806a81e
...
c665aa18ea
|
|
@ -2,9 +2,9 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/png" href="/favicon.png" sizes="96x96" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Hit Servers Manager</title>
|
<title>frontned</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.5 KiB |
|
|
@ -1,31 +1,3 @@
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,19 +5,18 @@ import { Dashboard } from './pages/Dashboard';
|
||||||
import { ProtectedRoute } from './routes/ProtectedRoute';
|
import { ProtectedRoute } from './routes/ProtectedRoute';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-bg text-text transition-colors duration-200 dark:bg-slate-950 dark:text-slate-100">
|
<BrowserRouter>
|
||||||
<BrowserRouter>
|
<Routes>
|
||||||
<Routes>
|
<Route path="/" element={
|
||||||
<Route path="/" element={
|
<ProtectedRoute>
|
||||||
<ProtectedRoute>
|
<Dashboard />
|
||||||
<Dashboard />
|
</ProtectedRoute>
|
||||||
</ProtectedRoute>
|
} />
|
||||||
} />
|
<Route path="/login" element={<Login />} />
|
||||||
<Route path="/login" element={<Login />} />
|
</Routes>
|
||||||
</Routes>
|
</BrowserRouter>
|
||||||
</BrowserRouter>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -254,5 +254,5 @@ export const Header = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
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",
|
wrapper: "flex items-center justify-between rounded-xl border border-cardBorder bg-card px-6 py-4 shadow-sm",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -74,11 +74,11 @@ export const ServerCardMetrics = () => {
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
grid: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
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 dark:border-slate-800 dark:from-slate-900 dark:to-slate-950 dark:shadow-slate-900/40",
|
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 dark:border-slate-800 dark:bg-slate-800/60",
|
iconWrapper: "flex h-14 w-14 items-center justify-center rounded-lg border border-accent/20 bg-accent/10 text-accent",
|
||||||
textGroup: "flex flex-col",
|
textGroup: "flex flex-col",
|
||||||
label: "text-xs font-medium uppercase tracking-wide text-text-secondary dark:text-slate-400",
|
label: "text-xs font-medium uppercase tracking-wide text-text-secondary",
|
||||||
value: "text-3xl font-semibold leading-tight text-text dark:text-white",
|
value: "text-3xl font-semibold text-text leading-tight",
|
||||||
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",
|
placeholder: "p-4 rounded-lg border border-cardBorder bg-card text-text-secondary text-sm",
|
||||||
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",
|
error: "p-4 rounded-lg border border-red-200 bg-red-50 text-red-600 text-sm",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ interface Props {
|
||||||
serverTypeOptions?: ServersType[];
|
serverTypeOptions?: ServersType[];
|
||||||
applicationOptions?: Applications[];
|
applicationOptions?: Applications[];
|
||||||
databaseOptions?: DatabaseType[];
|
databaseOptions?: DatabaseType[];
|
||||||
hideSensitive: boolean;
|
|
||||||
onToggleSensitive: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const withAllOption = <T extends string>(options?: T[]): Array<T | OptionAll> => {
|
const withAllOption = <T extends string>(options?: T[]): Array<T | OptionAll> => {
|
||||||
|
|
@ -39,8 +37,6 @@ export const ServersFilterBar = ({
|
||||||
serverTypeOptions,
|
serverTypeOptions,
|
||||||
applicationOptions,
|
applicationOptions,
|
||||||
databaseOptions,
|
databaseOptions,
|
||||||
hideSensitive,
|
|
||||||
onToggleSensitive,
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const typeOptions = withAllOption(serverTypeOptions);
|
const typeOptions = withAllOption(serverTypeOptions);
|
||||||
const applicationOptionsList = withAllOption(applicationOptions);
|
const applicationOptionsList = withAllOption(applicationOptions);
|
||||||
|
|
@ -77,11 +73,6 @@ export const ServersFilterBar = ({
|
||||||
onChange={(event) => onDbTypeChange(event.target.value as DatabaseType | OptionAll)}
|
onChange={(event) => onDbTypeChange(event.target.value as DatabaseType | OptionAll)}
|
||||||
options={databaseOptionsList}
|
options={databaseOptionsList}
|
||||||
/>
|
/>
|
||||||
<div className={Styles.toggleWrapper}>
|
|
||||||
<button type="button" className={Styles.toggleButton} onClick={onToggleSensitive}>
|
|
||||||
{hideSensitive ? "Mostrar dados confidenciais" : "Ocultar dados confidenciais"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -109,12 +100,10 @@ const Select = <T extends string>({ label, value, onChange, options }: SelectPro
|
||||||
};
|
};
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
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",
|
wrapper: "flex flex-wrap gap-4 rounded-lg border border-cardBorder bg-white/70 p-4 shadow-sm",
|
||||||
searchGroup: "flex-1 min-w-[220px]",
|
searchGroup: "flex-1 min-w-[220px]",
|
||||||
selectGroup: "flex min-w-[150px] flex-col gap-1",
|
selectGroup: "flex min-w-[150px] flex-col gap-1",
|
||||||
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary dark:text-slate-400",
|
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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
|
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 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",
|
||||||
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",
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useState } from "react";
|
||||||
import type { Server } from "../types/Server";
|
import type { Server } from "../types/Server";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -8,7 +9,6 @@ interface Props {
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
totalItems: number;
|
totalItems: number;
|
||||||
hideSensitive: boolean;
|
|
||||||
onPageChange: (page: number) => void;
|
onPageChange: (page: number) => void;
|
||||||
onPageSizeChange: (size: number) => void;
|
onPageSizeChange: (size: number) => void;
|
||||||
}
|
}
|
||||||
|
|
@ -23,36 +23,38 @@ export const ServersTable = ({
|
||||||
pageSize,
|
pageSize,
|
||||||
totalPages,
|
totalPages,
|
||||||
totalItems,
|
totalItems,
|
||||||
hideSensitive,
|
|
||||||
onPageChange,
|
onPageChange,
|
||||||
onPageSizeChange,
|
onPageSizeChange,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
const [hideSensitive, setHideSensitive] = useState(false);
|
||||||
const showingFrom = totalItems === 0 ? 0 : page * pageSize + 1;
|
const showingFrom = totalItems === 0 ? 0 : page * pageSize + 1;
|
||||||
const showingTo = totalItems === 0 ? 0 : Math.min((page + 1) * pageSize, totalItems);
|
const showingTo = totalItems === 0 ? 0 : Math.min((page + 1) * pageSize, totalItems);
|
||||||
const hasResults = servers.length > 0;
|
const hasResults = servers.length > 0;
|
||||||
const isInitialLoad = loading && !hasResults;
|
|
||||||
const showOverlay = loading && hasResults;
|
|
||||||
|
|
||||||
const formatSensitiveValue = (value: string | number) => (hideSensitive ? "••••" : value);
|
const formatSensitiveValue = (value: string | number) => (hideSensitive ? "••••" : value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={Styles.card}>
|
<div className={Styles.card}>
|
||||||
{isInitialLoad && <div className={Styles.status}>Carregando servidores...</div>}
|
<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>}
|
||||||
{error && <div className={Styles.error}>{error}</div>}
|
{error && <div className={Styles.error}>{error}</div>}
|
||||||
{!loading && !error && servers.length === 0 && (
|
{!loading && !error && servers.length === 0 && (
|
||||||
<div className={Styles.status}>Nenhum servidor encontrado.</div>
|
<div className={Styles.status}>Nenhum servidor encontrado.</div>
|
||||||
)}
|
)}
|
||||||
{hasResults && (
|
{!loading && !error && hasResults && (
|
||||||
<div className="relative space-y-4">
|
<>
|
||||||
{showOverlay && (
|
|
||||||
<div className={Styles.loadingOverlay}>
|
|
||||||
<span>Atualizando lista...</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={Styles.tableWrapper}>
|
<div className={Styles.tableWrapper}>
|
||||||
<table className={Styles.table}>
|
<table className={Styles.table}>
|
||||||
<thead className={Styles.tableHead}>
|
<thead className={Styles.tableHead}>
|
||||||
<tr className={Styles.tableHeadRow}>
|
<tr className="text-left">
|
||||||
<th className={Styles.tableHeadCell}>Nome</th>
|
<th className={Styles.tableHeadCell}>Nome</th>
|
||||||
<th className={Styles.tableHeadCell}>IP</th>
|
<th className={Styles.tableHeadCell}>IP</th>
|
||||||
<th className={Styles.tableHeadCell}>Porta</th>
|
<th className={Styles.tableHeadCell}>Porta</th>
|
||||||
|
|
@ -67,14 +69,14 @@ export const ServersTable = ({
|
||||||
{servers.map((server) => (
|
{servers.map((server) => (
|
||||||
<tr
|
<tr
|
||||||
key={server.id}
|
key={server.id}
|
||||||
className={Styles.tableRow}
|
className="hover:bg-gray-50 transition-colors even:bg-gray-50/50"
|
||||||
>
|
>
|
||||||
<td className={Styles.rowCell}>{server.name}</td>
|
<td className={Styles.rowCell}>{server.name}</td>
|
||||||
<td className={Styles.rowCell}>{formatSensitiveValue(server.ip)}</td>
|
<td className={Styles.rowCell}>{formatSensitiveValue(server.ip)}</td>
|
||||||
<td className={Styles.rowCell}>{formatSensitiveValue(server.port)}</td>
|
<td className={Styles.rowCell}>{formatSensitiveValue(server.port)}</td>
|
||||||
<td className={Styles.rowCell}>{formatSensitiveValue(server.user)}</td>
|
<td className={Styles.rowCell}>{formatSensitiveValue(server.user)}</td>
|
||||||
<td className={Styles.rowCell}>
|
<td className={Styles.rowCell}>
|
||||||
<code className={Styles.password}>
|
<code className="text-xs bg-gray-100 px-2 py-1 rounded">
|
||||||
{hideSensitive ? "••••" : server.password}
|
{hideSensitive ? "••••" : server.password}
|
||||||
</code>
|
</code>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -128,34 +130,29 @@ export const ServersTable = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className={Styles.footerSpacer} aria-hidden />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
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",
|
card: "relative bg-card border border-cardBorder shadow-sm rounded-lg overflow-hidden",
|
||||||
status: "p-4 text-sm text-text-secondary dark:text-slate-400",
|
status: "p-4 text-text-secondary text-sm",
|
||||||
error: "p-4 text-sm text-red-600 dark:text-red-400",
|
error: "p-4 text-red-600 text-sm",
|
||||||
tableWrapper: "overflow-x-auto rounded-lg border border-cardBorder shadow-sm dark:border-slate-800 dark:bg-slate-950/40",
|
tableWrapper: "overflow-x-auto rounded-lg shadow-sm border border-cardBorder",
|
||||||
table: "min-w-full table-auto divide-y divide-cardBorder dark:divide-slate-800",
|
table: "min-w-full divide-y divide-cardBorder table-auto",
|
||||||
tableHead: "sticky top-0 bg-gray-50 dark:bg-slate-800",
|
tableHead: "bg-gray-50 sticky top-0",
|
||||||
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",
|
||||||
tableHeadCell: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-text-secondary dark:text-slate-300",
|
tableBody: "bg-white divide-y divide-cardBorder",
|
||||||
tableBody: "divide-y divide-cardBorder bg-white dark:divide-slate-800 dark:bg-slate-900",
|
rowCell: "px-4 py-3 text-sm text-text",
|
||||||
tableRow: "transition-colors even:bg-gray-50/50 hover:bg-gray-50 dark:even:bg-slate-900 dark:hover:bg-slate-800",
|
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",
|
||||||
rowCell: "px-4 py-3 text-sm text-text dark:text-slate-100",
|
pageInfo: "text-sm text-text-secondary",
|
||||||
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",
|
paginationControls: "flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-4",
|
||||||
pageSizeLabel: "text-sm text-text dark:text-slate-100 flex items-center gap-2",
|
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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
|
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",
|
||||||
pageButtons: "flex items-center gap-3",
|
pageButtons: "flex items-center gap-3",
|
||||||
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",
|
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 dark:text-slate-400",
|
pageIndicator: "text-sm text-text-secondary",
|
||||||
footerSpacer: "h-4",
|
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",
|
||||||
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",
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,11 @@ export const Modal = ({ isOpen, title, onClose, children, description, bodyClass
|
||||||
};
|
};
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
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",
|
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 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",
|
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 border-b border-cardBorder pb-4 dark:border-slate-800",
|
header: "flex items-start justify-between gap-4 pb-4 border-b border-cardBorder",
|
||||||
title: "text-lg font-semibold text-text dark:text-white",
|
title: "text-lg font-semibold text-text",
|
||||||
closeButton: "text-2xl leading-none text-text-secondary hover:text-text dark:text-slate-400 dark:hover:text-white",
|
closeButton: "text-2xl leading-none text-text-secondary hover:text-text",
|
||||||
description: "pt-3 text-sm text-text-secondary dark:text-slate-400",
|
description: "pt-3 text-sm text-text-secondary",
|
||||||
body: "pt-4",
|
body: "pt-4",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export const BulkImportModal = ({
|
||||||
<Modal isOpen={isOpen} title="Cadastro em massa" onClose={onClose} bodyClassName={Styles.modalBody}>
|
<Modal isOpen={isOpen} title="Cadastro em massa" onClose={onClose} bodyClassName={Styles.modalBody}>
|
||||||
<div className={Styles.uploadCard}>
|
<div className={Styles.uploadCard}>
|
||||||
<label htmlFor="bulk-file" className={Styles.dropLabel}>
|
<label htmlFor="bulk-file" className={Styles.dropLabel}>
|
||||||
<span className="text-base font-medium text-text dark:text-white">Selecionar arquivo CSV</span>
|
<span className="text-base font-medium text-text">Selecionar arquivo CSV</span>
|
||||||
<span className="text-xs text-text-secondary">
|
<span className="text-xs text-text-secondary">
|
||||||
Arraste e solte ou clique para procurar
|
Arraste e solte ou clique para procurar
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -115,19 +115,19 @@ const Stat = ({
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
modalBody: "space-y-5",
|
modalBody: "space-y-5",
|
||||||
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",
|
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 text-text dark:text-white",
|
dropLabel: "flex flex-col gap-1 text-center",
|
||||||
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",
|
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 dark:text-slate-400",
|
helperText: "text-xs text-text-secondary",
|
||||||
actionsRow: "flex flex-wrap items-center gap-3",
|
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 dark:border-slate-700 dark:text-slate-100 dark:hover:bg-slate-800",
|
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 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
|
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 dark:text-red-400",
|
errorText: "text-sm text-red-600",
|
||||||
resultCard: "space-y-4 rounded-xl border border-cardBorder bg-white/90 p-5 dark:border-slate-800 dark:bg-slate-900/80",
|
resultCard: "rounded-xl border border-cardBorder bg-white/90 p-5 space-y-4",
|
||||||
statsGrid: "flex flex-wrap gap-4",
|
statsGrid: "flex flex-wrap gap-4",
|
||||||
failureList: "max-h-40 list-disc space-y-1 overflow-auto pl-5 text-sm text-text-secondary dark:text-slate-400",
|
failureList: "list-disc pl-5 space-y-1 text-sm text-text-secondary max-h-40 overflow-auto",
|
||||||
statBase: "flex flex-col rounded-lg border px-4 py-3 text-sm",
|
statBase: "flex flex-col rounded-lg border px-4 py-3 text-sm",
|
||||||
statDefault: "border-cardBorder bg-white text-text dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
|
statDefault: "border-cardBorder bg-white text-text",
|
||||||
statAccent: "border-accent/40 bg-accent/10 text-accent dark:border-accent/20 dark:bg-accent/20 dark:text-white",
|
statAccent: "border-accent/40 bg-accent/10 text-accent",
|
||||||
statDanger: "border-red-200 bg-red-50 text-red-600 dark:border-red-400/40 dark:bg-red-500/10 dark:text-red-300",
|
statDanger: "border-red-200 bg-red-50 text-red-600",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ export const HeaderActions = ({
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
actions: "flex items-center gap-3",
|
actions: "flex items-center gap-3",
|
||||||
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",
|
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 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",
|
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 transition-colors hover:bg-bg hover:text-text dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white",
|
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 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
|
logoutButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-hover",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,6 @@ export const HeaderBrand = () => {
|
||||||
const Styles = {
|
const Styles = {
|
||||||
brand: "flex items-center gap-3",
|
brand: "flex items-center gap-3",
|
||||||
logo: "h-10 w-10 object-contain",
|
logo: "h-10 w-10 object-contain",
|
||||||
title: "text-base font-semibold text-text dark:text-white",
|
title: "text-base font-semibold text-text",
|
||||||
subtitle: "text-xs uppercase tracking-wide text-text-secondary dark:text-slate-400",
|
subtitle: "text-xs uppercase tracking-wide text-text-secondary",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -65,14 +65,14 @@ export const ProfileModal = ({
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
modalBody: "space-y-4",
|
modalBody: "space-y-4",
|
||||||
helperText: "text-sm text-text-secondary dark:text-slate-400",
|
helperText: "text-sm text-text-secondary",
|
||||||
errorMessage: "text-sm text-red-600 dark:text-red-400",
|
errorMessage: "text-sm text-red-600",
|
||||||
form: "space-y-4",
|
form: "space-y-4",
|
||||||
formGrid: "grid gap-4 md:grid-cols-2",
|
formGrid: "grid gap-4 md:grid-cols-2",
|
||||||
field: "flex flex-col gap-2",
|
field: "flex flex-col gap-2",
|
||||||
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary dark:text-slate-400",
|
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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
|
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",
|
||||||
modalActions: "flex justify-end gap-3 pt-2",
|
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 dark:border-slate-700 dark:text-slate-100 dark:hover:bg-slate-800",
|
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 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
|
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,10 @@ const Styles = {
|
||||||
form: "space-y-4",
|
form: "space-y-4",
|
||||||
formGrid: "grid gap-4 md:grid-cols-2",
|
formGrid: "grid gap-4 md:grid-cols-2",
|
||||||
field: "flex flex-col gap-2",
|
field: "flex flex-col gap-2",
|
||||||
label: "text-xs font-semibold uppercase tracking-wide text-text-secondary dark:text-slate-400",
|
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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100",
|
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 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",
|
||||||
modalActions: "flex justify-end gap-3 pt-2",
|
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 dark:border-slate-700 dark:text-slate-100 dark:hover:bg-slate-800",
|
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 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200",
|
primaryButton: "rounded-md bg-accent px-4 py-2 text-sm font-semibold text-white hover:bg-hover disabled:opacity-70",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,12 @@ import { StrictMode } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
import { Toaster } from 'react-hot-toast';
|
import { Toaster } from 'react-hot-toast';
|
||||||
import { initSystemThemeWatcher } from './theme.ts';
|
|
||||||
|
|
||||||
initSystemThemeWatcher();
|
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<>
|
<>
|
||||||
<App />
|
<App />
|
||||||
<Toaster
|
<Toaster position="top-right" toastOptions={{ duration: 4000 }} />
|
||||||
position="top-right"
|
|
||||||
toastOptions={{
|
|
||||||
duration: 4000,
|
|
||||||
className: "bg-white text-text shadow-lg dark:!bg-slate-800 dark:!text-slate-100",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ export const Dashboard = () => {
|
||||||
const [pageSize, setPageSize] = useState<number>(10);
|
const [pageSize, setPageSize] = useState<number>(10);
|
||||||
const [totalPages, setTotalPages] = useState<number>(0);
|
const [totalPages, setTotalPages] = useState<number>(0);
|
||||||
const [totalItems, setTotalItems] = useState<number>(0);
|
const [totalItems, setTotalItems] = useState<number>(0);
|
||||||
const [hideSensitive, setHideSensitive] = useState<boolean>(true);
|
|
||||||
const [serverTypeOptions, setServerTypeOptions] = useState<string[]>([]);
|
const [serverTypeOptions, setServerTypeOptions] = useState<string[]>([]);
|
||||||
const [applicationOptions, setApplicationOptions] = useState<string[]>([]);
|
const [applicationOptions, setApplicationOptions] = useState<string[]>([]);
|
||||||
const [databaseOptions, setDatabaseOptions] = useState<string[]>([]);
|
const [databaseOptions, setDatabaseOptions] = useState<string[]>([]);
|
||||||
|
|
@ -151,16 +150,12 @@ export const Dashboard = () => {
|
||||||
setPageSize(nextSize);
|
setPageSize(nextSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggleSensitive = () => {
|
|
||||||
setHideSensitive((prev) => !prev);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchTypeOptions();
|
fetchTypeOptions();
|
||||||
}, [fetchTypeOptions]);
|
}, [fetchTypeOptions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout className="h-full py-10">
|
<Layout className="h-screen py-10">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Header
|
<Header
|
||||||
currentUser={currentUser}
|
currentUser={currentUser}
|
||||||
|
|
@ -181,8 +176,6 @@ export const Dashboard = () => {
|
||||||
onTypeChange={handleTypeChange}
|
onTypeChange={handleTypeChange}
|
||||||
onApplicationChange={handleApplicationChange}
|
onApplicationChange={handleApplicationChange}
|
||||||
onDbTypeChange={handleDbTypeChange}
|
onDbTypeChange={handleDbTypeChange}
|
||||||
hideSensitive={hideSensitive}
|
|
||||||
onToggleSensitive={handleToggleSensitive}
|
|
||||||
serverTypeOptions={serverTypeOptions}
|
serverTypeOptions={serverTypeOptions}
|
||||||
applicationOptions={applicationOptions}
|
applicationOptions={applicationOptions}
|
||||||
databaseOptions={databaseOptions}
|
databaseOptions={databaseOptions}
|
||||||
|
|
@ -195,7 +188,6 @@ export const Dashboard = () => {
|
||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
totalPages={totalPages}
|
totalPages={totalPages}
|
||||||
totalItems={totalItems}
|
totalItems={totalItems}
|
||||||
hideSensitive={hideSensitive}
|
|
||||||
onPageChange={handlePageChange}
|
onPageChange={handlePageChange}
|
||||||
onPageSizeChange={handlePageSizeChange}
|
onPageSizeChange={handlePageSizeChange}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,11 @@ export const Login = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout className={Styles.layout}>
|
<Layout className={Styles.layout}>
|
||||||
<img src="/logo.webp" alt="Logo" className="mx-auto mb-4 h-36 w-36" />
|
<img src="/logo.webp " alt="Logo" className="w-36 h-36 mx-auto mb-4" />
|
||||||
<div className={Styles.card}>
|
<div className={Styles.card}>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="email" className={Styles.label}>Email:</label>
|
<label htmlFor="email" className="block mb-2 text-md font-medium text-text">Email:</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
|
|
@ -65,7 +65,7 @@ export const Login = () => {
|
||||||
onChange={handleChange}/>
|
onChange={handleChange}/>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<label htmlFor="password" className={Styles.label}>Password:</label>
|
<label htmlFor="password" className="block mb-2 text-md font-medium text-text">Password:</label>
|
||||||
<input
|
<input
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
id="password"
|
id="password"
|
||||||
|
|
@ -84,7 +84,7 @@ export const Login = () => {
|
||||||
{showPassword ? <Eye size={18} /> : <EyeOff size={18} />}
|
{showPassword ? <Eye size={18} /> : <EyeOff size={18} />}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{error && <p className="mt-2 text-sm text-red-500 dark:text-red-400">{error}</p>}
|
{error && <p className="text-red-500 text-sm mt-2">{error}</p>}
|
||||||
<button type="submit" disabled={loading} className={Styles.button}>
|
<button type="submit" disabled={loading} className={Styles.button}>
|
||||||
{loading ? "Autenticando..." : "Login"}
|
{loading ? "Autenticando..." : "Login"}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -95,10 +95,9 @@ export const Login = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
layout: "flex h-screen animate-fade-up flex-col justify-center gap-4 px-4",
|
layout: "h-screen flex flex-col gap-4 justify-center animate-fade-up",
|
||||||
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",
|
card: "w-96 p-8 shadow-lg rounded-lg border border-cardBorder bg-card",
|
||||||
label: "mb-2 block text-md font-medium text-text dark:text-slate-100",
|
input: "bg-gray-50 border border-cardBorder text-text text-md rounded-lg outline-none block w-full p-2.5",
|
||||||
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-1/2 text-text p-1 focus:outline-none",
|
||||||
iconButton: "absolute right-3 top-9 p-1 text-text focus:outline-none dark:text-slate-200",
|
button: "w-full bg-accent p-2 rounded-md mt-4 text-white font-bold text-lg hover:bg-hover transition duration-150",
|
||||||
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",
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
const applyThemeToDocument = (isDark: boolean) => {
|
|
||||||
if (typeof document === "undefined") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const theme = isDark ? "dark" : "light";
|
|
||||||
const root = document.documentElement;
|
|
||||||
const body = document.body;
|
|
||||||
|
|
||||||
root.classList.toggle("dark", isDark);
|
|
||||||
root.setAttribute("data-theme", theme);
|
|
||||||
|
|
||||||
if (body) {
|
|
||||||
body.classList.toggle("dark", isDark);
|
|
||||||
body.setAttribute("data-theme", theme);
|
|
||||||
body.style.colorScheme = isDark ? "dark" : "light";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const initSystemThemeWatcher = () => {
|
|
||||||
if (typeof window === "undefined" || typeof window.matchMedia === "undefined") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
||||||
const applyMatch = (matches: boolean) => applyThemeToDocument(matches);
|
|
||||||
const handleChange = (event: MediaQueryListEvent) => applyMatch(event.matches);
|
|
||||||
|
|
||||||
applyMatch(mediaQuery.matches);
|
|
||||||
if (typeof mediaQuery.addEventListener === "function") {
|
|
||||||
mediaQuery.addEventListener("change", handleChange);
|
|
||||||
} else if (typeof mediaQuery.addListener === "function") {
|
|
||||||
mediaQuery.addListener(handleChange);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,15 +1,5 @@
|
||||||
const withOpacity = (variable) => {
|
|
||||||
return ({ opacityValue } = {}) => {
|
|
||||||
if (opacityValue !== undefined) {
|
|
||||||
return `rgb(var(${variable}) / ${opacityValue})`;
|
|
||||||
}
|
|
||||||
return `rgb(var(${variable}))`;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
darkMode: "class",
|
|
||||||
content: [
|
content: [
|
||||||
"./index.html",
|
"./index.html",
|
||||||
"./src/**/*.{js,ts,jsx,tsx}",
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
|
|
@ -17,13 +7,13 @@ export default {
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
bg: withOpacity("--color-bg"),
|
bg: '#FAFAF9',
|
||||||
card: withOpacity("--color-card"),
|
card: '#F4F4F2',
|
||||||
cardBorder: withOpacity("--color-card-border"),
|
cardBorder: '#E5E7EB',
|
||||||
text: withOpacity("--color-text"),
|
text: '#1A1A1A',
|
||||||
"text-secondary": withOpacity("--color-text-secondary"),
|
'text-secondary': '#6B7280',
|
||||||
hover: withOpacity("--color-hover"),
|
hover: '#D04A0F',
|
||||||
accent: withOpacity("--color-accent"),
|
accent: '#E95A1B',
|
||||||
},
|
},
|
||||||
keyframes: {
|
keyframes: {
|
||||||
'fade-up': {
|
'fade-up': {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue