mirror of
https://github.com/house-of-vanity/OutFleet.git
synced 2025-10-24 09:19:09 +00:00
feat: added nav
This commit is contained in:
1
client/src/components/index.ts
Normal file
1
client/src/components/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './nav-menu';
|
||||||
1
client/src/components/nav-menu/index.ts
Normal file
1
client/src/components/nav-menu/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './nav-menu';
|
||||||
31
client/src/components/nav-menu/nav-menu.tsx
Normal file
31
client/src/components/nav-menu/nav-menu.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Link, useLocation } from 'react-router';
|
||||||
|
import { clsx } from 'clsx';
|
||||||
|
|
||||||
|
interface NavMenuItems {
|
||||||
|
href: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NavMenuProps {
|
||||||
|
items: NavMenuItems[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NavMenu = (props: NavMenuProps) => {
|
||||||
|
const { items } = props;
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="tabs">
|
||||||
|
{items.map(({ href, label }) => (
|
||||||
|
<Link
|
||||||
|
className={clsx('tab', {
|
||||||
|
active: href === pathname,
|
||||||
|
})}
|
||||||
|
to={href}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
52
client/src/pages/certificates/certificates.tsx
Normal file
52
client/src/pages/certificates/certificates.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import type { RouteObject } from 'react-router';
|
||||||
|
|
||||||
|
export const Certificates = () => {
|
||||||
|
return (
|
||||||
|
<div id="certificates" className="tab-content active">
|
||||||
|
<div className="section">
|
||||||
|
<h2>Add Certificate</h2>
|
||||||
|
<form id="certificateForm">
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Name:</label>
|
||||||
|
<input type="text" id="certName" required />
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Domain:</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="certDomain"
|
||||||
|
placeholder="example.com"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Certificate Type:</label>
|
||||||
|
<select id="certType" required>
|
||||||
|
<option value="self_signed">Self-Signed</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="certAutoRenew" checked /> Auto Renew
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" className="btn btn-primary">
|
||||||
|
Generate Certificate
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="section">
|
||||||
|
<h2>Certificates List</h2>
|
||||||
|
<div id="certificatesList" className="loading">
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CertificatesRoute: RouteObject = {
|
||||||
|
path: '/certificates',
|
||||||
|
Component: Certificates,
|
||||||
|
};
|
||||||
1
client/src/pages/certificates/index.ts
Normal file
1
client/src/pages/certificates/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './certificates';
|
||||||
29
client/src/pages/dashboard/dashboard.tsx
Normal file
29
client/src/pages/dashboard/dashboard.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import type { RouteObject } from 'react-router';
|
||||||
|
|
||||||
|
export const Dashboard = () => {
|
||||||
|
return (
|
||||||
|
<div id="dashboard" className="tab-content active">
|
||||||
|
<div className="section">
|
||||||
|
<h2>Statistics</h2>
|
||||||
|
<p>
|
||||||
|
Servers: <span id="serverCount">Loading...</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Templates: <span id="templateCount">Loading...</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Certificates: <span id="certCount">Loading...</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Users: <span id="userCount">Loading...</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DashboardRoute: RouteObject = {
|
||||||
|
index: true,
|
||||||
|
path: '/',
|
||||||
|
Component: Dashboard,
|
||||||
|
};
|
||||||
1
client/src/pages/dashboard/index.ts
Normal file
1
client/src/pages/dashboard/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './dashboard';
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Outlet } from 'react-router';
|
import { Outlet } from 'react-router';
|
||||||
import './home.css';
|
import './home.css';
|
||||||
|
import { NavMenu } from '../../components/nav-menu/nav-menu';
|
||||||
|
import { navItems } from './utils';
|
||||||
|
|
||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
return (
|
return (
|
||||||
@@ -10,266 +12,21 @@ export const Home = () => {
|
|||||||
{/* <!-- Toast notifications container --> */}
|
{/* <!-- Toast notifications container --> */}
|
||||||
<div className="toast-container" id="toastContainer"></div>
|
<div className="toast-container" id="toastContainer"></div>
|
||||||
|
|
||||||
<div className="tabs">
|
<NavMenu items={navItems} />
|
||||||
<div
|
|
||||||
className="tab active"
|
|
||||||
// onClick="showTab('dashboard')"
|
|
||||||
>
|
|
||||||
Dashboard
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="tab"
|
|
||||||
// onClick="showTab('servers')"
|
|
||||||
>
|
|
||||||
Servers
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="tab"
|
|
||||||
//onClick="showTab('templates')"
|
|
||||||
>
|
|
||||||
Inbound Templates
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="tab"
|
|
||||||
// onClick="showTab('certificates')"
|
|
||||||
>
|
|
||||||
Certificates
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="tab"
|
|
||||||
// onClick="showTab('inbounds')"
|
|
||||||
>
|
|
||||||
Inbound Binding
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="tab"
|
|
||||||
// onClick="showTab('users')"
|
|
||||||
>
|
|
||||||
Users
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <!-- Dashboard --> */}
|
{/* <!-- Dashboard --> */}
|
||||||
<div id="dashboard" className="tab-content active">
|
|
||||||
<div className="section">
|
<Outlet />
|
||||||
<h2>Statistics</h2>
|
|
||||||
<p>
|
|
||||||
Servers: <span id="serverCount">Loading...</span>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Templates: <span id="templateCount">Loading...</span>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Certificates: <span id="certCount">Loading...</span>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Users: <span id="userCount">Loading...</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <!-- Servers --> */}
|
{/* <!-- Servers --> */}
|
||||||
<div id="servers" className="tab-content">
|
|
||||||
<div className="section">
|
|
||||||
<h2>Add Server</h2>
|
|
||||||
<form id="serverForm">
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Name:</label>
|
|
||||||
<input type="text" id="serverName" required />
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Hostname:</label>
|
|
||||||
<input type="text" id="serverHostname" required />
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>gRPC Port:</label>
|
|
||||||
<input type="number" id="serverPort" value="2053" />
|
|
||||||
</div>
|
|
||||||
<button type="submit" className="btn btn-primary">
|
|
||||||
Add Server
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="section">
|
|
||||||
<h2>Servers List</h2>
|
|
||||||
<div id="serversList" className="loading">
|
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <!-- Templates --> */}
|
{/* <!-- Templates --> */}
|
||||||
<div id="templates" className="tab-content">
|
|
||||||
<div className="section">
|
|
||||||
<h2>Add Template</h2>
|
|
||||||
<form id="templateForm">
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Name:</label>
|
|
||||||
<input type="text" id="templateName" required />
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Protocol:</label>
|
|
||||||
<select id="templateProtocol" required>
|
|
||||||
<option value="vless">VLESS</option>
|
|
||||||
<option value="vmess">VMess</option>
|
|
||||||
<option value="trojan">Trojan</option>
|
|
||||||
<option value="shadowsocks">Shadowsocks</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Default Port:</label>
|
|
||||||
<input type="number" id="templatePort" value="443" required />
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="templateTls" /> Requires TLS
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Configuration Template:</label>
|
|
||||||
<textarea
|
|
||||||
id="templateConfig"
|
|
||||||
rows={6}
|
|
||||||
style={{
|
|
||||||
width: '300px',
|
|
||||||
}}
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
<button type="submit" className="btn btn-primary">
|
|
||||||
Add Template
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="section">
|
|
||||||
<h2>Templates List</h2>
|
|
||||||
<div id="templatesList" className="loading">
|
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <!-- Certificates --> */}
|
{/* <!-- Certificates --> */}
|
||||||
<div id="certificates" className="tab-content">
|
|
||||||
<div className="section">
|
|
||||||
<h2>Add Certificate</h2>
|
|
||||||
<form id="certificateForm">
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Name:</label>
|
|
||||||
<input type="text" id="certName" required />
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Domain:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="certDomain"
|
|
||||||
placeholder="example.com"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Certificate Type:</label>
|
|
||||||
<select id="certType" required>
|
|
||||||
<option value="self_signed">Self-Signed</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="certAutoRenew" checked /> Auto
|
|
||||||
Renew
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<button type="submit" className="btn btn-primary">
|
|
||||||
Generate Certificate
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="section">
|
|
||||||
<h2>Certificates List</h2>
|
|
||||||
<div id="certificatesList" className="loading">
|
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <!-- Server Inbounds --> */}
|
{/* <!-- Server Inbounds --> */}
|
||||||
<div id="inbounds" className="tab-content">
|
|
||||||
<div className="section">
|
|
||||||
<h2>Bind Template to Server</h2>
|
|
||||||
<form id="inboundForm">
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Server:</label>
|
|
||||||
<select id="inboundServer" required>
|
|
||||||
<option value="">Select Server...</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Template:</label>
|
|
||||||
<select id="inboundTemplate" required>
|
|
||||||
<option value="">Select Template...</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Port:</label>
|
|
||||||
<input type="number" id="inboundPort" value="443" required />
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Certificate:</label>
|
|
||||||
<select id="inboundCertificate">
|
|
||||||
<option value="">No Certificate</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="inboundActive" checked /> Active
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<button type="submit" className="btn btn-primary">
|
|
||||||
Bind Template
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="section">
|
|
||||||
<h2>Server Inbounds</h2>
|
|
||||||
<div id="inboundsList" className="loading">
|
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <!-- Users --> */}
|
{/* <!-- Users --> */}
|
||||||
<div id="users" className="tab-content">
|
|
||||||
<div className="section">
|
|
||||||
<h2>Add User</h2>
|
|
||||||
<form id="userForm">
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Name:</label>
|
|
||||||
<input type="text" id="userName" required />
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Comment:</label>
|
|
||||||
<input type="text" id="userComment" />
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label>Telegram ID:</label>
|
|
||||||
<input type="number" id="userTelegram" />
|
|
||||||
</div>
|
|
||||||
<button type="submit" className="btn btn-primary">
|
|
||||||
Add User
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="section">
|
|
||||||
<h2>Users List</h2>
|
|
||||||
<div id="usersList" className="loading">
|
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <!-- Modal dialogs --> */}
|
{/* <!-- Modal dialogs --> */}
|
||||||
@@ -333,7 +90,6 @@ export const Home = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Outlet />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
26
client/src/pages/home/utils.ts
Normal file
26
client/src/pages/home/utils.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export const navItems = [
|
||||||
|
{
|
||||||
|
href: '/',
|
||||||
|
label: 'Dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/servers',
|
||||||
|
label: 'Servers',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/inbound-templates',
|
||||||
|
label: 'Inbound Templates',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/certificates',
|
||||||
|
label: 'Certificates',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/inbound-binding',
|
||||||
|
label: 'Inbound Binding',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: '/users',
|
||||||
|
label: 'Users',
|
||||||
|
},
|
||||||
|
];
|
||||||
55
client/src/pages/inbound-binding/inbound-binding.tsx
Normal file
55
client/src/pages/inbound-binding/inbound-binding.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import type { RouteObject } from 'react-router';
|
||||||
|
|
||||||
|
export const InboundBinding = () => {
|
||||||
|
return (
|
||||||
|
<div id="inbounds" className="tab-content active">
|
||||||
|
<div className="section">
|
||||||
|
<h2>Bind Template to Server</h2>
|
||||||
|
<form id="inboundForm">
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Server:</label>
|
||||||
|
<select id="inboundServer" required>
|
||||||
|
<option value="">Select Server...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Template:</label>
|
||||||
|
<select id="inboundTemplate" required>
|
||||||
|
<option value="">Select Template...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Port:</label>
|
||||||
|
<input type="number" id="inboundPort" value="443" required />
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Certificate:</label>
|
||||||
|
<select id="inboundCertificate">
|
||||||
|
<option value="">No Certificate</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="inboundActive" checked /> Active
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" className="btn btn-primary">
|
||||||
|
Bind Template
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="section">
|
||||||
|
<h2>Server Inbounds</h2>
|
||||||
|
<div id="inboundsList" className="loading">
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InboundBindingRoute: RouteObject = {
|
||||||
|
path: '/inbound-binding',
|
||||||
|
Component: InboundBinding,
|
||||||
|
};
|
||||||
1
client/src/pages/inbound-binding/index.ts
Normal file
1
client/src/pages/inbound-binding/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './inbound-binding';
|
||||||
60
client/src/pages/inbound-templates/inboud-templates.tsx
Normal file
60
client/src/pages/inbound-templates/inboud-templates.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import type { RouteObject } from 'react-router';
|
||||||
|
|
||||||
|
export const InboundTemplates = () => {
|
||||||
|
return (
|
||||||
|
<div id="templates" className="tab-content active">
|
||||||
|
<div className="section">
|
||||||
|
<h2>Add Template</h2>
|
||||||
|
<form id="templateForm">
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Name:</label>
|
||||||
|
<input type="text" id="templateName" required />
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Protocol:</label>
|
||||||
|
<select id="templateProtocol" required>
|
||||||
|
<option value="vless">VLESS</option>
|
||||||
|
<option value="vmess">VMess</option>
|
||||||
|
<option value="trojan">Trojan</option>
|
||||||
|
<option value="shadowsocks">Shadowsocks</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Default Port:</label>
|
||||||
|
<input type="number" id="templatePort" value="443" required />
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="templateTls" /> Requires TLS
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Configuration Template:</label>
|
||||||
|
<textarea
|
||||||
|
id="templateConfig"
|
||||||
|
rows={6}
|
||||||
|
style={{
|
||||||
|
width: '300px',
|
||||||
|
}}
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" className="btn btn-primary">
|
||||||
|
Add Template
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="section">
|
||||||
|
<h2>Templates List</h2>
|
||||||
|
<div id="templatesList" className="loading">
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InboundTemplatesRoute: RouteObject = {
|
||||||
|
path: '/inbound-templates',
|
||||||
|
Component: InboundTemplates,
|
||||||
|
};
|
||||||
1
client/src/pages/inbound-templates/index.ts
Normal file
1
client/src/pages/inbound-templates/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './inboud-templates';
|
||||||
@@ -1 +1,7 @@
|
|||||||
export * from './home';
|
export * from './home';
|
||||||
|
export * from './certificates';
|
||||||
|
export * from './dashboard';
|
||||||
|
export * from './inbound-binding';
|
||||||
|
export * from './inbound-templates';
|
||||||
|
export * from './servers';
|
||||||
|
export * from './users';
|
||||||
|
|||||||
1
client/src/pages/servers/index.ts
Normal file
1
client/src/pages/servers/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './servers';
|
||||||
40
client/src/pages/servers/servers.tsx
Normal file
40
client/src/pages/servers/servers.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type { RouteObject } from 'react-router';
|
||||||
|
|
||||||
|
export const Servers = () => {
|
||||||
|
return (
|
||||||
|
<div id="servers" className="tab-content active">
|
||||||
|
<div className="section">
|
||||||
|
<h2>Add Server</h2>
|
||||||
|
<form id="serverForm">
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Name:</label>
|
||||||
|
<input type="text" id="serverName" required />
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Hostname:</label>
|
||||||
|
<input type="text" id="serverHostname" required />
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>gRPC Port:</label>
|
||||||
|
<input type="number" id="serverPort" value="2053" />
|
||||||
|
</div>
|
||||||
|
<button type="submit" className="btn btn-primary">
|
||||||
|
Add Server
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="section">
|
||||||
|
<h2>Servers List</h2>
|
||||||
|
<div id="serversList" className="loading">
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ServersRoute: RouteObject = {
|
||||||
|
path: '/servers',
|
||||||
|
Component: Servers,
|
||||||
|
};
|
||||||
1
client/src/pages/users/index.ts
Normal file
1
client/src/pages/users/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './users';
|
||||||
40
client/src/pages/users/users.tsx
Normal file
40
client/src/pages/users/users.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type { RouteObject } from 'react-router';
|
||||||
|
|
||||||
|
export const Users = () => {
|
||||||
|
return (
|
||||||
|
<div id="users" className="tab-content active">
|
||||||
|
<div className="section">
|
||||||
|
<h2>Add User</h2>
|
||||||
|
<form id="userForm">
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Name:</label>
|
||||||
|
<input type="text" id="userName" required />
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Comment:</label>
|
||||||
|
<input type="text" id="userComment" />
|
||||||
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Telegram ID:</label>
|
||||||
|
<input type="number" id="userTelegram" />
|
||||||
|
</div>
|
||||||
|
<button type="submit" className="btn btn-primary">
|
||||||
|
Add User
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="section">
|
||||||
|
<h2>Users List</h2>
|
||||||
|
<div id="usersList" className="loading">
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UsersRoute: RouteObject = {
|
||||||
|
path: '/users',
|
||||||
|
Component: Users,
|
||||||
|
};
|
||||||
@@ -1,9 +1,25 @@
|
|||||||
import { createBrowserRouter } from 'react-router';
|
import { createBrowserRouter } from 'react-router';
|
||||||
import { Home } from '../pages';
|
import {
|
||||||
|
Home,
|
||||||
|
DashboardRoute,
|
||||||
|
ServersRoute,
|
||||||
|
InboundTemplatesRoute,
|
||||||
|
CertificatesRoute,
|
||||||
|
InboundBindingRoute,
|
||||||
|
UsersRoute,
|
||||||
|
} from '../pages';
|
||||||
|
|
||||||
export const router = createBrowserRouter([
|
export const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
Component: Home,
|
Component: Home,
|
||||||
|
children: [
|
||||||
|
DashboardRoute,
|
||||||
|
ServersRoute,
|
||||||
|
InboundTemplatesRoute,
|
||||||
|
CertificatesRoute,
|
||||||
|
InboundBindingRoute,
|
||||||
|
UsersRoute,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user