28295e346c
- Created a global TypeScript declaration for the txadmin interface on the Window object. - Implemented date formatting utilities using date-fns with Spanish locale support. - Added TypeScript configuration files for app, Electron, and Node environments. - Set up Vite configuration for React application.
100 lines
3.5 KiB
TypeScript
100 lines
3.5 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { HashRouter, Routes, Route } from 'react-router-dom';
|
|
import { Minus, Square, X } from 'lucide-react';
|
|
import { DashboardLayout } from './components/layout/DashboardLayout';
|
|
import { DashboardPage } from './pages/DashboardPage';
|
|
import { UsersPage } from './pages/UsersPage';
|
|
import { WapServersPage } from './pages/WapServersPage';
|
|
import { CompaniesPage } from './pages/CompaniesPage';
|
|
import { PlansPage } from './pages/PlansPage';
|
|
import txadminIcon from './assets/txadmin-icon.png';
|
|
|
|
const TitleBar = () => (
|
|
<header className="app-titlebar">
|
|
<div className="app-titlebar-brand">
|
|
<img className="app-titlebar-logo" src={txadminIcon} alt="txadmin" />
|
|
<span>txadmin</span>
|
|
</div>
|
|
<div className="app-titlebar-actions">
|
|
<button type="button" aria-label="Minimize window" onClick={() => void window.txadmin.minimizeWindow()}>
|
|
<Minus size={16} />
|
|
</button>
|
|
<button type="button" aria-label="Maximize window" onClick={() => void window.txadmin.toggleMaximizeWindow()}>
|
|
<Square size={13} />
|
|
</button>
|
|
<button type="button" className="close" aria-label="Close window" onClick={() => void window.txadmin.closeWindow()}>
|
|
<X size={16} />
|
|
</button>
|
|
</div>
|
|
</header>
|
|
);
|
|
|
|
function App() {
|
|
const [keyStatus, setKeyStatus] = useState<{ loaded: boolean; path: string | null }>({ loaded: false, path: null });
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
if (!window.txadmin) {
|
|
setError('Desktop bridge is not available. Restart txadmin and try again.');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
window.txadmin.getKeyStatus()
|
|
.then(setKeyStatus)
|
|
.catch(() => setError('Could not read private key status.'))
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
const selectPrivateKey = async () => {
|
|
setError(null);
|
|
try {
|
|
setKeyStatus(await window.txadmin.selectPrivateKey());
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Could not load the private key.');
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return <div className="app-shell"><TitleBar /><div className="key-gate">Loading txadmin...</div></div>;
|
|
}
|
|
|
|
if (!keyStatus.loaded) {
|
|
return (
|
|
<div className="app-shell">
|
|
<TitleBar />
|
|
<div className="key-gate">
|
|
<div className="glass-panel key-gate-card">
|
|
<img className="key-gate-logo" src={txadminIcon} alt="txadmin" />
|
|
<h1>txadmin</h1>
|
|
<p>Load the sysadmin private key to unlock admin functions.</p>
|
|
<button className="primary-button" onClick={selectPrivateKey}>Select Private Key</button>
|
|
<p className="key-gate-hint">Expected equivalent: sysadmin/keys/tx-sysadmin-private.key</p>
|
|
{error && <p className="key-gate-error">{error}</p>}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="app-shell">
|
|
<TitleBar />
|
|
<HashRouter>
|
|
<Routes>
|
|
<Route path="/" element={<DashboardLayout keyPath={keyStatus.path} onSelectPrivateKey={selectPrivateKey} />}>
|
|
<Route index element={<DashboardPage />} />
|
|
<Route path="users" element={<UsersPage />} />
|
|
<Route path="companies" element={<CompaniesPage />} />
|
|
<Route path="plans" element={<PlansPage />} />
|
|
<Route path="wapservers" element={<WapServersPage />} />
|
|
</Route>
|
|
</Routes>
|
|
</HashRouter>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|