diff --git a/frontend/src/tools/devtools.jsx b/frontend/src/tools/devtools.jsx index 3bd7837..3843bfd 100644 --- a/frontend/src/tools/devtools.jsx +++ b/frontend/src/tools/devtools.jsx @@ -99,7 +99,7 @@ function CronExplainer() { ))} @@ -107,7 +107,7 @@ function CronExplainer() {
-
BEDEUTET
+
BEDEUTET
{result.human}
{result.fields && ( @@ -274,7 +274,7 @@ function CronBuilder() { function ElektroInp({ vals, setVals, k, label, unit, placeholder }) { return (
-
+
{label} [{unit}]
setVals(p=>({...p,[k]:e.target.value}))} @@ -368,7 +368,7 @@ function ElektroRechner({ mobile }) { return (
{/* Info */} -
Felder ausfüllen → Ergebnis erscheint automatisch · Mindestens 2 bekannte Größen eingeben
@@ -400,7 +400,7 @@ function ElektroRechner({ mobile }) { - :
Mindestens 2 Werte eingeben
} + :
Mindestens 2 Werte eingeben
} {/* Wirkungsgrad */} @@ -423,7 +423,7 @@ function ElektroRechner({ mobile }) {
-
+
Strompreis [€/kWh]
setPreis(e.target.value)} @@ -477,6 +477,236 @@ function ElektroRechner({ mobile }) { ); } +// ─────────────────────────────────────────────────────────────── Datetime ── +const MOSES_EPOCH = new Date('1970-01-01T00:00:00Z'); // Moseszeit = Unix time (Sekunden) + +function pad2(n){ return String(n).padStart(2,'0'); } + +function toLocalISO(d){ + return `${d.getFullYear()}-${pad2(d.getMonth()+1)}-${pad2(d.getDate())}T${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`; +} + +function formatDE(d){ + return `${pad2(d.getDate())}.${pad2(d.getMonth()+1)}.${d.getFullYear()} ${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`; +} + +function DtRow({ label, value, muted }) { + return ( +
+ {label} + {value} +
+ ); +} + +function DatetimeRechner({ mobile }) { + const now = new Date(); + + // ── Tab 1: Umrechner ── + const [conv, setConv] = useState({ unix:'', local:'', de:'' }); + const [convResult, setConvResult] = useState(null); + + function convFromUnix(val) { + const n = parseInt(val); + if (isNaN(n)) { setConvResult(null); return; } + const d = new Date(n * 1000); + setConvResult({ + de: formatDE(d), + local: toLocalISO(d), + unix: String(n), + wday: WEEKDAYS[d.getDay()], + kw: getKW(d), + }); + } + + function convFromLocal(val) { + if (!val) { setConvResult(null); return; } + const d = new Date(val); + if (isNaN(d)) { setConvResult(null); return; } + setConvResult({ + de: formatDE(d), + local: toLocalISO(d), + unix: String(Math.floor(d.getTime()/1000)), + wday: WEEKDAYS[d.getDay()], + kw: getKW(d), + }); + } + + function getKW(d) { + const tmp = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate())); + tmp.setUTCDate(tmp.getUTCDate() + 4 - (tmp.getUTCDay()||7)); + const y = new Date(Date.UTC(tmp.getUTCFullYear(),0,1)); + return Math.ceil((((tmp-y)/86400000)+1)/7); + } + + // ── Tab 2: Geburtstag ── + const [bday, setBday] = useState(''); + const [bdayResult, setBdayResult] = useState(null); + + function calcBday(val) { + setBday(val); + if (!val) { setBdayResult(null); return; } + const birth = new Date(val); + if (isNaN(birth)) { setBdayResult(null); return; } + const today = new Date(); + let age = today.getFullYear() - birth.getFullYear(); + const hadBday = today.getMonth() > birth.getMonth() || + (today.getMonth() === birth.getMonth() && today.getDate() >= birth.getDate()); + if (!hadBday) age--; + + const nextBday = new Date(today.getFullYear(), birth.getMonth(), birth.getDate()); + if (nextBday <= today) nextBday.setFullYear(today.getFullYear()+1); + const diffMs = nextBday - today; + const diffD = Math.ceil(diffMs / 86400000); + const totalD = Math.floor((today - birth) / 86400000); + + setBdayResult({ age, diffD, totalD, wday: WEEKDAYS[birth.getDay()], + geburtstag: `${pad2(birth.getDate())}.${pad2(birth.getMonth()+1)}.${birth.getFullYear()}`, + nextDate: `${pad2(nextBday.getDate())}.${pad2(nextBday.getMonth()+1)}.${nextBday.getFullYear()}`, + }); + } + + // ── Tab 3: Zeitdifferenz ── + const [dtA, setDtA] = useState(''); + const [dtB, setDtB] = useState(''); + const [diffResult, setDiffResult] = useState(null); + + function calcDiff(a, b) { + if (!a || !b) { setDiffResult(null); return; } + const dA = new Date(a), dB = new Date(b); + if (isNaN(dA)||isNaN(dB)) { setDiffResult(null); return; } + const ms = Math.abs(dB - dA); + const s = Math.floor(ms/1000); + const m = Math.floor(s/60); + const h = Math.floor(m/60); + const d = Math.floor(h/24); + const weeks = Math.floor(d/7); + const months = Math.abs(dB.getMonth()-dA.getMonth() + (dB.getFullYear()-dA.getFullYear())*12); + setDiffResult({ ms, s, m, h, d, weeks, months }); + } + + const inp = { ...S.inp, fontSize:13, padding:'7px 9px', width:'100%', boxSizing:'border-box' }; + const lbl = { color:'rgba(255,255,255,0.55)', fontFamily:'monospace', fontSize:10, marginBottom:4 }; + + const [tab, setTab] = useState('conv'); + const tabs = [{id:'conv',label:'⇄ Umrechnen'},{id:'bday',label:'🎂 Geburtstag'},{id:'diff',label:'⏱ Differenz'}]; + + return ( +
+
+ {tabs.map(t=>( + + ))} +
+ + {tab==='conv' && ( +
+
+
+
Unix / Moseszeit (Sekunden)
+ { setConv(p=>({...p,unix:e.target.value})); convFromUnix(e.target.value); }}/> +
+
+
Datum & Uhrzeit
+ { setConv(p=>({...p,local:e.target.value})); convFromLocal(e.target.value); }}/> +
+
+ + {convResult && ( +
+ + + + + +
+ )} + {!convResult && ( +
+ Unix-Timestamp oder Datum eingeben → alle Formate werden berechnet +
+ )} +
+
BEISPIEL
+
+ Moseszeit 1685554093 = 31.05.2026 17:28:13 +
+
+
+ )} + + {tab==='bday' && ( +
+
+
Geburtsdatum
+ calcBday(e.target.value)}/> +
+ {bdayResult && ( +
+ + + + + +
+ )} + {!bdayResult && ( +
+ Geburtsdatum eingeben +
+ )} +
+ )} + + {tab==='diff' && ( +
+
+
+
Von
+ { setDtA(e.target.value); calcDiff(e.target.value, dtB); }}/> +
+
+
Bis
+ { setDtB(e.target.value); calcDiff(dtA, e.target.value); }}/> +
+
+ {diffResult && ( +
+ + + + + + + +
+ )} + {!diffResult && ( +
+ Zwei Zeitpunkte eingeben +
+ )} +
+ )} +
+ ); +} + const TOOL_DEFS = [ {id:'cron', label:'⏱ Crontab', desc:'Crontab erklären und erstellen', ready:true, tabs:[{id:'explain',label:'Erklären'},{id:'build',label:'Erstellen'}], @@ -487,7 +717,7 @@ const TOOL_DEFS = [ {id:'regex', label:'🔍 Regex', desc:'Regex testen', ready:false}, {id:'diff', label:'± Text Diff', desc:'Texte vergleichen', ready:false}, {id:'jwt', label:'🔑 JWT', desc:'JWT dekodieren', ready:false}, - {id:'timestamp', label:'🕐 Timestamp', desc:'Unix Timestamps umrechnen', ready:false}, + {id:'datetime', label:'📅 Datetime', desc:'Zeiten umrechnen · Geburtstag · Differenz', ready:true, component:DatetimeRechner}, {id:'color', label:'🎨 Farben', desc:'HEX/RGB/HSL umrechnen', ready:false}, ]; @@ -501,7 +731,7 @@ export default function DevTools({ toast, mobile }) {

Dev-Tools

- Konverter · Formatter · Helfer + Konverter · Formatter · Helfer
{TOOL_DEFS.map(t=>( @@ -509,7 +739,7 @@ export default function DevTools({ toast, mobile }) { onClick={()=>{ if(t.ready){setActiveTool(t.id);setActiveTab(t.tabs?.[0]?.id||'');} else toast('Kommt bald 🚧'); }} style={{padding:'8px 14px',borderRadius:10,fontFamily:'monospace',fontSize:11,cursor:t.ready?'pointer':'default', background:activeTool===t.id?'#4ecdc4':t.ready?'rgba(255,255,255,0.06)':'rgba(255,255,255,0.02)', - color: activeTool===t.id?'#0d0d0f':t.ready?'rgba(255,255,255,0.65)':'rgba(255,255,255,0.2)', + color: activeTool===t.id?'#0d0d0f':t.ready?'rgba(255,255,255,0.65)':'rgba(255,255,255,0.4)', border: activeTool===t.id?'none':t.ready?'1px solid rgba(255,255,255,0.1)':'1px solid rgba(255,255,255,0.05)', fontWeight: activeTool===t.id?700:400}}> {t.label}{!t.ready&&bald} @@ -519,7 +749,7 @@ export default function DevTools({ toast, mobile }) {
{active?.label}
- {active?.desc} + {active?.desc}
{active?.tabs&&(
diff --git a/frontend/src/tools/skizze.jsx b/frontend/src/tools/skizze.jsx index b4cb76d..a760887 100644 --- a/frontend/src/tools/skizze.jsx +++ b/frontend/src/tools/skizze.jsx @@ -158,7 +158,7 @@ function Shape({ s, selected, onClick }) { // ── Properties Panel ────────────────────────────────────────────────────────── function PropsPanel({ shape, onChange, onDelete, vars, scale, onScaleChange }) { if (!shape) return ( -
+
Form auswählen oder zeichnen.
V=Auswählen R=Rect L=Linie C=Kreis
@@ -207,7 +207,7 @@ function PropsPanel({ shape, onChange, onDelete, vars, scale, onScaleChange }) {
onScaleChange(parseFloat(e.target.value)||1)} step={0.1} min={0.1} style={{...S.inp,flex:1,fontSize:12,padding:'5px 8px'}}/> - mm/px + mm/px
@@ -386,7 +386,7 @@ export default function Skizze({ toast, mobile }) { border: tool===t.id?'none':'1px solid rgba(255,255,255,0.1)', }}>{t.label} ))} - + {TOOLS.find(t=>t.id===tool)?.title}
@@ -433,7 +433,7 @@ export default function Skizze({ toast, mobile }) { {Object.entries(vars).map(([k,v])=>( {setFormula(f=>f+k);formulaRef.current?.focus();}}> {k} - ={typeof v==='number'?v.toFixed(2):v} + ={typeof v==='number'?v.toFixed(2):v} ))}
@@ -463,7 +463,7 @@ export default function Skizze({ toast, mobile }) { {/* Ergebnisse */} {results.length===0&&Object.keys(vars).length===0&&( -
+
Formen zeichnen → benennen → hier rechnen
)} @@ -482,7 +482,7 @@ export default function Skizze({ toast, mobile }) { + color:'rgba(255,255,255,0.5)',fontSize:12,padding:'0 2px',lineHeight:1}}>✕
))}