75062dbf5e
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
125 lines
4.1 KiB
React
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;
|