mirror of
https://github.com/skoelle/PeopleCostCounter.git
synced 2026-09-17 20:00:25 +00:00
de styling in en version
This commit is contained in:
+177
-71
@@ -10,62 +10,120 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300..700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
/* same styles as variant2 (kept compact) */
|
||||
:root, [data-theme="light"] { --color-bg:#f7f6f2;--color-surface:#f9f8f5;--color-surface-2:#fbfbf9;--color-surface-offset:#f3f0ec;--color-divider:#dcd9d5;--color-border:#d4d1ca;--color-text:#28251d;--color-text-muted:#7a7974;--color-text-faint:#bab9b4;--color-primary:#01696f;--color-primary-hover:#0c4e54;--color-warning:#964219;--color-success:#437a22;--shadow-sm:0 1px 2px rgba(0,0,0,0.06);--shadow-md:0 4px 12px rgba(0,0,0,0.08);--radius-md:.5rem;--radius-lg:.75rem;--radius-xl:1rem;--text-xs:.75rem;--text-sm:0.9rem;--text-base:1rem;--space-3:.75rem;--space-4:1rem;--space-6:1.5rem;--space-8:2rem;--font-body:'Inter',system-ui,sans-serif;--font-mono:'JetBrains Mono',monospace}
|
||||
[data-theme="dark"]{--color-bg:#171614;--color-surface:#1c1b19;--color-text:#cdccca}
|
||||
*{box-sizing:border-box;margin:0;padding:0} body{min-height:100dvh;font-family:var(--font-body);font-size:var(--text-base);color:var(--color-text);background:var(--color-bg);display:flex;flex-direction:column;align-items:center;padding:var(--space-8) var(--space-4)}
|
||||
:root, [data-theme="light"] {
|
||||
--color-bg:#f7f6f2;--color-surface:#f9f8f5;--color-surface-2:#fbfbf9;
|
||||
--color-surface-offset:#f3f0ec;--color-divider:#dcd9d5;--color-border:#d4d1ca;
|
||||
--color-text:#28251d;--color-text-muted:#7a7974;--color-text-faint:#bab9b4;
|
||||
--color-primary:#01696f;--color-primary-hover:#0c4e54;
|
||||
--color-warning:#964219;--color-success:#437a22;
|
||||
--shadow-sm:0 1px 2px oklch(0.2 0.01 80/0.06);
|
||||
--shadow-md:0 4px 12px oklch(0.2 0.01 80/0.08);
|
||||
--shadow-lg:0 12px 32px oklch(0.2 0.01 80/0.12);
|
||||
--radius-sm:.375rem;--radius-md:.5rem;--radius-lg:.75rem;--radius-xl:1rem;--radius-full:9999px;
|
||||
--text-xs:clamp(.75rem,.7rem + .25vw,.875rem);
|
||||
--text-sm:clamp(.875rem,.8rem + .35vw,1rem);
|
||||
--text-base:clamp(1rem,.95rem + .25vw,1.125rem);
|
||||
--text-lg:clamp(1.125rem,1rem + .75vw,1.5rem);
|
||||
--text-xl:clamp(1.5rem,1.2rem + 1.25vw,2.25rem);
|
||||
--space-1:.25rem;--space-2:.5rem;--space-3:.75rem;--space-4:1rem;
|
||||
--space-5:1.25rem;--space-6:1.5rem;--space-8:2rem;
|
||||
--font-body:'Inter','Helvetica Neue',sans-serif;
|
||||
--font-mono:'JetBrains Mono','Courier New',monospace;
|
||||
--transition:180ms cubic-bezier(.16,1,.3,1);
|
||||
}
|
||||
[data-theme="dark"] {
|
||||
--color-bg:#171614;--color-surface:#1c1b19;--color-surface-2:#201f1d;
|
||||
--color-surface-offset:#22211f;--color-divider:#262523;--color-border:#393836;
|
||||
--color-text:#cdccca;--color-text-muted:#797876;--color-text-faint:#5a5957;
|
||||
--color-primary:#4f98a3;--color-primary-hover:#227f8b;
|
||||
--color-warning:#bb653b;--color-success:#6daa45;
|
||||
--shadow-sm:0 1px 2px oklch(0 0 0/.2);
|
||||
--shadow-md:0 4px 12px oklch(0 0 0/.3);
|
||||
--shadow-lg:0 12px 32px oklch(0 0 0/.4);
|
||||
}
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
html{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
|
||||
body{min-height:100dvh;font-family:var(--font-body);font-size:var(--text-base);color:var(--color-text);background:var(--color-bg);display:flex;flex-direction:column;align-items:center;padding:var(--space-8) var(--space-4)}
|
||||
input,button{font:inherit;color:inherit}
|
||||
a,button,input{transition:color var(--transition),background var(--transition),border-color var(--transition),box-shadow var(--transition)}
|
||||
button{cursor:pointer;background:none;border:none}
|
||||
.app-header{width:100%;max-width:720px;display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-8)}
|
||||
.logo-text{font-size:1.125rem;font-weight:600}
|
||||
.logo-sub{font-size:0.85rem;color:var(--color-text-muted)}
|
||||
.theme-toggle{width:36px;height:36px;border-radius:.5rem;display:flex;align-items:center;justify-content:center;border:1px solid rgba(0,0,0,0.06);background:transparent}
|
||||
.card{width:100%;max-width:720px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-xl);padding:var(--space-8);box-shadow:var(--shadow-md)}
|
||||
.form-row{display:grid;grid-template-columns:1fr 1fr auto;gap:1rem;align-items:end;margin-bottom:1.5rem}
|
||||
.form-field{display:flex;flex-direction:column;gap:.5rem}
|
||||
.form-label{font-size:.75rem;color:var(--color-text-muted);text-transform:uppercase}
|
||||
.form-input{padding:.75rem 1rem;background:var(--color-bg);border:1px solid var(--color-border);border-radius:.5rem}
|
||||
.input-prefix{position:absolute;left:1rem;top:50%;transform:translateY(-50%);color:var(--color-text-muted)}
|
||||
.btn-start{padding:.6rem 1.2rem;background:var(--color-primary);color:#fff;border-radius:.5rem;display:flex;align-items:center;gap:.5rem}
|
||||
.btn-reset{padding:.4rem .75rem;border:1px solid var(--color-border);border-radius:.5rem}
|
||||
.divider{height:1px;background:var(--color-divider);margin:1.25rem 0}
|
||||
.status-badge{display:inline-flex;align-items:center;gap:.5rem;padding:.25rem .75rem;border-radius:9999px;background:var(--color-surface-offset);color:var(--color-text-muted);border:1px solid var(--color-border)}
|
||||
.running-indicator{width:10px;height:10px;border-radius:9999px;background:currentColor}
|
||||
.running-indicator.active{animation:pulse 1.2s infinite}@keyframes pulse{0%{opacity:1}50%{opacity:.3}100%{opacity:1}}
|
||||
.cost-display-wrap{position:relative;background:var(--color-surface-offset);border:1px solid var(--color-border);border-radius:1rem;padding:2rem;text-align:center;min-height:160px;display:flex;flex-direction:column;align-items:center;justify-content:center}
|
||||
.cost-display-amount{font-family:var(--font-mono);font-size:clamp(2.5rem,6vw,4.5rem);font-weight:700}
|
||||
.info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:1rem;margin-top:1.5rem}
|
||||
.info-tile{background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:.5rem;padding:1rem}
|
||||
.info-tile-label{font-size:.75rem;color:var(--color-text-muted);text-transform:uppercase;margin-bottom:.5rem}
|
||||
.info-tile-value{font-family:var(--font-mono);font-weight:600}
|
||||
.logo{display:flex;align-items:center;gap:var(--space-3)}
|
||||
.logo-icon{color:var(--color-primary)}
|
||||
.logo-text{font-size:var(--text-lg);font-weight:600;color:var(--color-text);letter-spacing:-.02em}
|
||||
.logo-sub{font-size:var(--text-xs);color:var(--color-text-muted);margin-top:1px}
|
||||
.theme-toggle{width:36px;height:36px;border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;color:var(--color-text-muted);border:1px solid oklch(from var(--color-text) l c h/.1)}
|
||||
.theme-toggle:hover{color:var(--color-text);background:var(--color-surface)}
|
||||
.card{width:100%;max-width:720px;background:var(--color-surface);border:1px solid oklch(from var(--color-text) l c h/.08);border-radius:var(--radius-xl);padding:var(--space-8);box-shadow:var(--shadow-md)}
|
||||
.form-row{display:grid;grid-template-columns:1fr 1fr auto;gap:var(--space-4);align-items:end;margin-bottom:var(--space-6)}
|
||||
@media(max-width:600px){.form-row{grid-template-columns:1fr}}
|
||||
.form-field{display:flex;flex-direction:column;gap:var(--space-2)}
|
||||
.form-label{font-size:var(--text-xs);font-weight:600;color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.06em}
|
||||
.form-input{padding:var(--space-3) var(--space-4);background:var(--color-bg);border:1px solid var(--color-border);border-radius:var(--radius-md);font-size:var(--text-base);color:var(--color-text);outline:none;width:100%}
|
||||
.form-input:focus{border-color:var(--color-primary);box-shadow:0 0 0 3px oklch(from var(--color-primary) l c h/.15)}
|
||||
.form-input:disabled{opacity:.5;cursor:not-allowed}
|
||||
.form-hint{font-size:var(--text-xs);color:var(--color-text-faint);margin-top:var(--space-1)}
|
||||
.input-prefix-wrap{position:relative}
|
||||
.input-prefix-wrap .form-input{padding-left:var(--space-8)}
|
||||
.input-prefix{position:absolute;left:var(--space-3);top:50%;transform:translateY(-50%);color:var(--color-text-muted);font-size:var(--text-sm);pointer-events:none;line-height:1}
|
||||
.btn-start{padding:var(--space-3) var(--space-6);background:var(--color-primary);color:#fff;border-radius:var(--radius-md);font-size:var(--text-sm);font-weight:600;white-space:nowrap;display:flex;align-items:center;gap:var(--space-2);box-shadow:var(--shadow-sm);height:44px}
|
||||
.btn-start:hover{background:var(--color-primary-hover);box-shadow:var(--shadow-md)}
|
||||
.btn-start:active{transform:scale(.98)}
|
||||
.btn-reset{padding:var(--space-2) var(--space-4);border:1px solid var(--color-border);border-radius:var(--radius-md);font-size:var(--text-xs);color:var(--color-text-muted);font-weight:500;display:flex;align-items:center;gap:var(--space-2)}
|
||||
.btn-reset:hover{color:var(--color-text);border-color:var(--color-text-muted)}
|
||||
.divider{height:1px;background:var(--color-divider);margin:var(--space-6) 0}
|
||||
.action-row{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--space-3);margin-bottom:var(--space-6)}
|
||||
.status-badge{display:inline-flex;align-items:center;gap:var(--space-2);padding:var(--space-1) var(--space-3);border-radius:var(--radius-full);font-size:var(--text-xs);font-weight:600;letter-spacing:.04em;text-transform:uppercase;background:var(--color-surface-offset);color:var(--color-text-muted);border:1px solid var(--color-border)}
|
||||
.status-badge.running{background:color-mix(in oklch,var(--color-warning) 12%,var(--color-surface));color:var(--color-warning);border-color:color-mix(in oklch,var(--color-warning) 30%,var(--color-border))}
|
||||
.status-badge.stopped{background:color-mix(in oklch,var(--color-success) 10%,var(--color-surface));color:var(--color-success);border-color:color-mix(in oklch,var(--color-success) 30%,var(--color-border))}
|
||||
.running-indicator{width:10px;height:10px;border-radius:var(--radius-full);background:currentColor;display:inline-block}
|
||||
@keyframes pulse{0%{opacity:1}50%{opacity:.3}100%{opacity:1}}
|
||||
.running-indicator.active{animation:pulse 1.2s ease-in-out infinite}
|
||||
.cost-display-wrap{position:relative;background:var(--color-surface-offset);border:1px solid oklch(from var(--color-text) l c h/.06);border-radius:var(--radius-lg);padding:var(--space-8) var(--space-8) var(--space-6);text-align:center;min-height:160px;display:flex;flex-direction:column;align-items:center;justify-content:center}
|
||||
.cost-display-label{font-size:var(--text-xs);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.08em;font-weight:600;margin-bottom:var(--space-3)}
|
||||
.cost-display-amount{font-family:var(--font-mono);font-size:clamp(2.5rem,6vw,4.5rem);font-weight:600;letter-spacing:-.03em;color:var(--color-text);line-height:1;transition:color .3s ease}
|
||||
.cost-display-amount.running{color:var(--color-warning)}
|
||||
.cost-display-currency{font-size:var(--text-xl);font-weight:400;color:var(--color-text-muted);vertical-align:super;margin-right:var(--space-2);font-family:var(--font-mono)}
|
||||
.cost-display-elapsed{margin-top:var(--space-3);font-size:var(--text-xs);color:var(--color-text-faint);font-family:var(--font-mono)}
|
||||
.info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:var(--space-3);margin-top:var(--space-6)}
|
||||
.info-tile{background:var(--color-surface-2,var(--color-surface));border:1px solid oklch(from var(--color-text) l c h/.06);border-radius:var(--radius-md);padding:var(--space-4)}
|
||||
.info-tile-label{font-size:var(--text-xs);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.05em;font-weight:600;margin-bottom:var(--space-2)}
|
||||
.info-tile-value{font-family:var(--font-mono);font-size:var(--text-sm);font-weight:600;color:var(--color-text)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="app-header">
|
||||
<div style="display:flex;align-items:center;gap:1rem">
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" aria-hidden="true"><circle cx="16" cy="16" r="14" stroke="currentColor" stroke-width="2"/><path d="M16 8v8l5 3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><circle cx="16" cy="16" r="2" fill="currentColor"/></svg>
|
||||
<div class="logo">
|
||||
<svg class="logo-icon" width="32" height="32" viewBox="0 0 32 32" fill="none" aria-label="Meeting Cost Tracker">
|
||||
<circle cx="16" cy="16" r="14" stroke="currentColor" stroke-width="2"/>
|
||||
<path d="M16 8v8l5 3" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="16" cy="16" r="2" fill="currentColor"/>
|
||||
</svg>
|
||||
<div>
|
||||
<div class="logo-text">Meeting Cost Tracker</div>
|
||||
<div class="logo-sub">Live meeting cost overview</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="theme-toggle" data-theme-toggle aria-label="Toggle theme">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/></svg>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
||||
</svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main class="card">
|
||||
<div class="form-row">
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="devCount">Developers present</label>
|
||||
<input class="form-input" type="number" id="devCount" min="1" max="500" value="5" aria-label="Developers present">
|
||||
<div style="font-size:.85rem;color:var(--color-text-muted)">number of participants</div>
|
||||
<input class="form-input" type="number" id="devCount" min="1" max="500" value="5">
|
||||
<div class="form-hint">Number of participants</div>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label class="form-label" for="avgSalary">Avg. monthly gross salary</label>
|
||||
<div style="position:relative">
|
||||
<div class="input-prefix-wrap">
|
||||
<span class="input-prefix">EUR</span>
|
||||
<input class="form-input" type="number" id="avgSalary" min="1000" max="50000" value="5000" style="padding-left:3.5rem" aria-label="Average monthly salary">
|
||||
<input class="form-input" type="number" id="avgSalary" min="1000" max="50000" value="5000" style="padding-left:3.5rem">
|
||||
</div>
|
||||
<div style="font-size:.85rem;color:var(--color-text-muted)">brutto, average salary</div>
|
||||
<div class="form-hint">Brutto, average salary</div>
|
||||
</div>
|
||||
<button class="btn-start" id="mainBtn" onclick="handleBtn()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" id="btnIcon"><polygon points="5,3 19,12 5,21"/></svg>
|
||||
@@ -73,43 +131,77 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem">
|
||||
<span class="status-badge" id="statusBadge"><span class="running-indicator" id="runIndicator"></span> Ready</span>
|
||||
<button class="btn-reset" id="resetBtn" onclick="resetTimer()" style="display:none">Reset</button>
|
||||
<div class="action-row">
|
||||
<span class="status-badge" id="statusBadge">
|
||||
<span class="running-indicator" id="runIndicator"></span>
|
||||
Ready
|
||||
</span>
|
||||
<button class="btn-reset" id="resetBtn" onclick="resetTimer()" style="display:none">
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="cost-display-wrap">
|
||||
<div style="font-size:.75rem;color:var(--color-text-muted);text-transform:uppercase;margin-bottom:.5rem">Total cost so far</div>
|
||||
<div class="cost-display-amount" id="costDisplay"><span style="font-family:var(--font-mono);font-size:1rem;margin-right:.4rem">EUR</span><span id="costValue">0.00</span></div>
|
||||
<div style="margin-top:.5rem;font-size:.85rem;color:var(--color-text-muted)" id="elapsedDisplay">Not started</div>
|
||||
<div class="cost-display-label">Total cost so far</div>
|
||||
<div class="cost-display-amount" id="costDisplay">
|
||||
<span class="cost-display-currency">EUR</span><span id="costValue">0.00</span>
|
||||
</div>
|
||||
<div class="cost-display-elapsed" id="elapsedDisplay">Not started</div>
|
||||
</div>
|
||||
|
||||
<div class="info-grid" id="infoGrid" style="display:none">
|
||||
<div class="info-tile"><div class="info-tile-label">Cost / second</div><div class="info-tile-value" id="tilePerSec">-</div></div>
|
||||
<div class="info-tile"><div class="info-tile-label">Cost / minute</div><div class="info-tile-value" id="tilePerMin">-</div></div>
|
||||
<div class="info-tile"><div class="info-tile-label">Cost / hour</div><div class="info-tile-value" id="tilePerHour">-</div></div>
|
||||
<div class="info-tile"><div class="info-tile-label">People × salary</div><div class="info-tile-value" id="tileTotal">-</div></div>
|
||||
<div class="info-tile"><div class="info-tile-label">Annual salary (per person)</div><div class="info-tile-value" id="tileAnnual">-</div></div>
|
||||
<div class="info-tile"><div class="info-tile-label">incl. employer contributions (×1.2)</div><div class="info-tile-value" id="tileEmployer">-</div></div>
|
||||
<div class="info-tile"><div class="info-tile-label">Total annual cost</div><div class="info-tile-value" id="tileAnnualTotal">-</div></div>
|
||||
<div class="info-tile">
|
||||
<div class="info-tile-label">Cost / second</div>
|
||||
<div class="info-tile-value" id="tilePerSec">-</div>
|
||||
</div>
|
||||
<div class="info-tile">
|
||||
<div class="info-tile-label">Cost / minute</div>
|
||||
<div class="info-tile-value" id="tilePerMin">-</div>
|
||||
</div>
|
||||
<div class="info-tile">
|
||||
<div class="info-tile-label">Cost / hour</div>
|
||||
<div class="info-tile-value" id="tilePerHour">-</div>
|
||||
</div>
|
||||
<div class="info-tile">
|
||||
<div class="info-tile-label">People x salary</div>
|
||||
<div class="info-tile-value" id="tileTotal">-</div>
|
||||
</div>
|
||||
<div class="info-tile">
|
||||
<div class="info-tile-label">Annual salary (per person)</div>
|
||||
<div class="info-tile-value" id="tileAnnual">-</div>
|
||||
</div>
|
||||
<div class="info-tile">
|
||||
<div class="info-tile-label">incl. employer contributions (x1.2)</div>
|
||||
<div class="info-tile-value" id="tileEmployer">-</div>
|
||||
</div>
|
||||
<div class="info-tile">
|
||||
<div class="info-tile-label">Total annual cost</div>
|
||||
<div class="info-tile-value" id="tileAnnualTotal">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
var root=document.documentElement; var dark=false; var btn=document.querySelector('[data-theme-toggle]');
|
||||
var root=document.documentElement;
|
||||
var dark=false;
|
||||
var btn=document.querySelector('[data-theme-toggle]');
|
||||
var sunSvg='<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>';
|
||||
var moonSvg='<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
|
||||
if(btn){ root.setAttribute('data-theme', dark ? 'dark' : 'light'); btn.innerHTML = dark ? sunSvg : moonSvg; btn.addEventListener('click', function(){ dark=!dark; root.setAttribute('data-theme', dark ? 'dark' : 'light'); btn.innerHTML = dark ? sunSvg : moonSvg; }); }
|
||||
if(btn){
|
||||
root.setAttribute('data-theme', dark ? 'dark' : 'light');
|
||||
btn.innerHTML = dark ? sunSvg : moonSvg;
|
||||
btn.addEventListener('click', function(){
|
||||
dark = !dark; root.setAttribute('data-theme', dark ? 'dark' : 'light'); btn.innerHTML = dark ? sunSvg : moonSvg;
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
var timer=null,isRunning=false,startTime=null,pausedElapsed=0;
|
||||
// English formatting (GBP/GB style but currency EUR)
|
||||
function fmt(n){return new Intl.NumberFormat('en-GB',{style:'currency',currency:'EUR',minimumFractionDigits:2,maximumFractionDigits:2}).format(n);}
|
||||
function fmtPlain(n,d){return n.toLocaleString('en-GB',{minimumFractionDigits:d,maximumFractionDigits:d});}
|
||||
function fmtElapsed(sec){ var h=Math.floor(sec/3600), m=Math.floor((sec%3600)/60), s=Math.floor(sec%60); if(h>0) return h + 'h ' + String(m).padStart(2,'0') + 'm ' + String(s).padStart(2,'0') + 's'; if(m>0) return m + 'm ' + String(s).padStart(2,'0') + 's'; return Math.floor(sec) + 's elapsed'; }
|
||||
|
||||
function fmtElapsed(sec){
|
||||
var h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60),s=Math.floor(sec%60);
|
||||
if(h>0)return h+'h '+String(m).padStart(2,'0')+'m '+String(s).padStart(2,'0')+'s';
|
||||
if(m>0)return m+'m '+String(s).padStart(2,'0')+'s';
|
||||
return Math.floor(sec)+'s elapsed';
|
||||
}
|
||||
function getParams(){
|
||||
var devCount=Math.max(1,parseInt(document.getElementById('devCount').value)||1);
|
||||
var avgSalary=Math.max(0,parseFloat(document.getElementById('avgSalary').value)||5000);
|
||||
@@ -119,7 +211,6 @@
|
||||
var costPerSecond=totalCost/220/8/60/60;
|
||||
return{devCount:devCount,avgSalary:avgSalary,annualSalary:annualSalary,annualWithEmployer:annualWithEmployer,totalCost:totalCost,costPerSecond:costPerSecond};
|
||||
}
|
||||
|
||||
function tick(){
|
||||
var elapsed=pausedElapsed+(Date.now()-startTime)/1000;
|
||||
var p=getParams();
|
||||
@@ -130,37 +221,52 @@
|
||||
document.getElementById('tilePerMin').textContent='EUR '+fmtPlain(p.costPerSecond*60,2);
|
||||
document.getElementById('tilePerHour').textContent='EUR '+fmtPlain(p.costPerSecond*3600,2);
|
||||
document.getElementById('tileTotal').textContent=p.devCount+' x EUR '+fmtPlain(p.avgSalary,2);
|
||||
// annual / employer / total
|
||||
document.getElementById('tileAnnual').textContent = fmtPlain(p.annualSalary,0) + ' €';
|
||||
document.getElementById('tileEmployer').textContent = fmtPlain(p.annualWithEmployer,0) + ' €';
|
||||
document.getElementById('tileAnnualTotal').textContent = fmtPlain(p.totalCost,0) + ' €';
|
||||
var annual = p.annualSalary;
|
||||
var employer = p.annualWithEmployer;
|
||||
var annualTotal = p.totalCost;
|
||||
document.getElementById('tileAnnual').textContent=fmtPlain(annual,0)+' EUR';
|
||||
document.getElementById('tileEmployer').textContent=fmtPlain(employer,0)+' EUR';
|
||||
document.getElementById('tileAnnualTotal').textContent=fmtPlain(annualTotal,0)+' EUR';
|
||||
}
|
||||
|
||||
function handleBtn(){isRunning?pauseTimer():startTimer();}
|
||||
|
||||
function startTimer(){
|
||||
isRunning=true; startTime=Date.now(); timer=setInterval(tick,100);
|
||||
isRunning=true;startTime=Date.now();
|
||||
timer=setInterval(tick,100);
|
||||
document.getElementById('btnLabel').textContent='Pause';
|
||||
document.getElementById('btnIcon').innerHTML='<rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/>';
|
||||
document.getElementById('costDisplay').classList.add('running');
|
||||
document.getElementById('statusBadge').className='status-badge running';
|
||||
document.getElementById('statusBadge').innerHTML='<span class="running-indicator active"></span> Running';
|
||||
document.getElementById('resetBtn').style.display='inline-flex';
|
||||
document.getElementById('resetBtn').style.display='flex';
|
||||
document.getElementById('infoGrid').style.display='grid';
|
||||
document.getElementById('devCount').disabled=true; document.getElementById('avgSalary').disabled=true;
|
||||
document.getElementById('devCount').disabled=true;
|
||||
document.getElementById('avgSalary').disabled=true;
|
||||
tick();
|
||||
}
|
||||
|
||||
function pauseTimer(){
|
||||
isRunning=false; pausedElapsed += (Date.now()-startTime)/1000; clearInterval(timer); timer=null;
|
||||
isRunning=false;pausedElapsed+=(Date.now()-startTime)/1000;
|
||||
clearInterval(timer);timer=null;
|
||||
document.getElementById('btnLabel').textContent='Resume';
|
||||
document.getElementById('btnIcon').innerHTML='<polygon points="5,3 19,12 5,21"/>';
|
||||
document.getElementById('costDisplay').classList.remove('running');
|
||||
document.getElementById('statusBadge').className='status-badge stopped';
|
||||
document.getElementById('statusBadge').innerHTML='<span class="running-indicator"></span> Paused';
|
||||
}
|
||||
|
||||
function resetTimer(){ if(timer) clearInterval(timer); timer=null; isRunning=false; pausedElapsed=0; startTime=null; document.getElementById('costValue').textContent='0.00'; document.getElementById('elapsedDisplay').textContent='Not started'; document.getElementById('btnLabel').textContent='Start'; document.getElementById('btnIcon').innerHTML='<polygon points="5,3 19,12 5,21"/>'; document.getElementById('costDisplay').classList.remove('running'); document.getElementById('statusBadge').className='status-badge'; document.getElementById('statusBadge').innerHTML='<span class="running-indicator"></span> Ready'; document.getElementById('resetBtn').style.display='none'; document.getElementById('infoGrid').style.display='none'; document.getElementById('devCount').disabled=false; document.getElementById('avgSalary').disabled=false; }
|
||||
function resetTimer(){
|
||||
if(timer)clearInterval(timer);
|
||||
timer=null;isRunning=false;pausedElapsed=0;startTime=null;
|
||||
document.getElementById('costValue').textContent='0.00';
|
||||
document.getElementById('elapsedDisplay').textContent='Not started';
|
||||
document.getElementById('btnLabel').textContent='Start';
|
||||
document.getElementById('btnIcon').innerHTML='<polygon points="5,3 19,12 5,21"/>';
|
||||
document.getElementById('costDisplay').classList.remove('running');
|
||||
document.getElementById('statusBadge').className='status-badge';
|
||||
document.getElementById('statusBadge').innerHTML='<span class="running-indicator"></span> Ready';
|
||||
document.getElementById('resetBtn').style.display='none';
|
||||
document.getElementById('infoGrid').style.display='none';
|
||||
document.getElementById('devCount').disabled=false;
|
||||
document.getElementById('avgSalary').disabled=false;
|
||||
}
|
||||
|
||||
// input listeners: update per-second display when editing
|
||||
document.getElementById('devCount').addEventListener('input', function(){ if(!isRunning){ var p=getParams(); if(p.costPerSecond>0) document.getElementById('tilePerSec').textContent='EUR '+p.costPerSecond.toLocaleString('en-GB',{minimumFractionDigits:4,maximumFractionDigits:4}); }});
|
||||
|
||||
Reference in New Issue
Block a user