Files
Tools/frontend/src/components/IpCalcTool.jsx
T

125 lines
4.1 KiB
React

import { useState } from 'react';
import axios from '../services/api';
const EXAMPLES = ['192.168.0.0/24', '10.0.0.0/8', '172.16.0.0/12'];
const INFO_FIELDS = [
{ key: 'network', label: 'Netzwerkadresse' },
{ key: 'broadcast', label: 'Broadcast-Adresse' },
{ key: 'netmask', label: 'Subnetzmaske' },
{ key: 'wildcard', label: 'Wildcard-Maske' },
{ key: 'first_host', label: 'Erste nutzbare IP' },
{ key: 'last_host', label: 'Letzte nutzbare IP' },
{ key: 'total_hosts', label: 'Nutzbare Hosts' },
{ key: 'prefix_length', label: 'Präfixlänge', format: (v) => `/${v}` },
{ key: 'ip_class', label: 'IP-Klasse' },
];
function IpCalcTool() {
const [cidr, setCidr] = useState('');
const [result, setResult] = useState(null);
const [error, setError] = useState('');
const [copiedKey, setCopiedKey] = useState('');
const calculate = async (val) => {
const input = val !== undefined ? val : cidr;
setError('');
setResult(null);
if (!input.trim()) {
setError('Bitte CIDR eingeben.');
return;
}
try {
const res = await axios.post('/api/ip/calculate', { cidr: input });
setResult(res.data);
} catch (err) {
setError(err.response?.data?.message || 'Fehler bei der Berechnung');
}
};
const useExample = (val) => {
setCidr(val);
setError('');
setResult(null);
calculate(val);
};
const copy = (key, text) => {
navigator.clipboard.writeText(String(text));
setCopiedKey(key);
setTimeout(() => setCopiedKey(''), 1500);
};
return (
<div className="main-content">
<h2>IP / Subnetz Rechner</h2>
<p style={{ color: 'var(--muted)', marginBottom: '16px', fontSize: '0.95rem' }}>
CIDR-Netzwerke berechnen und analysieren.
</p>
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap', marginBottom: '10px' }}>
{EXAMPLES.map((ex) => (
<button key={ex} className="ghost" onClick={() => useExample(ex)}
style={{ fontFamily: 'monospace', fontSize: '0.85rem' }}>
{ex}
</button>
))}
</div>
<div style={{ display: 'flex', gap: '8px' }}>
<input
type="text"
value={cidr}
onChange={(e) => { setCidr(e.target.value); setResult(null); setError(''); }}
placeholder="192.168.1.0/24"
style={{ fontFamily: 'monospace', flex: 1 }}
onKeyDown={(e) => e.key === 'Enter' && calculate()}
/>
<button onClick={() => calculate()} style={{ flexShrink: 0 }}>Berechnen</button>
</div>
{error && <p className="error" style={{ marginTop: '12px' }}>{error}</p>}
{result && (
<div style={{
marginTop: '16px',
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))',
gap: '10px',
}}>
{INFO_FIELDS.map(({ key, label, format }) => {
const value = format ? format(result[key]) : result[key];
return (
<div key={key} style={{
padding: '12px 14px',
background: 'var(--surface-2)',
border: '1px solid var(--border)',
borderRadius: '12px',
display: 'flex',
flexDirection: 'column',
gap: '4px',
}}>
<span style={{ fontSize: '0.78rem', color: 'var(--muted)', fontWeight: 600 }}>{label}</span>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px' }}>
<span style={{ fontFamily: 'monospace', color: 'var(--text)', fontSize: '0.95rem', wordBreak: 'break-all' }}>
{String(value)}
</span>
<button
className="ghost"
onClick={() => copy(key, value)}
style={{ flexShrink: 0, margin: 0, padding: '4px 10px', fontSize: '0.78rem' }}
>
{copiedKey === key ? '✓' : 'Kopieren'}
</button>
</div>
</div>
);
})}
</div>
)}
</div>
);
}
export default IpCalcTool;