Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>Compact Story Point Estimator</title>
- <style>
- body {
- font-family: Arial, sans-serif;
- background: #f9fafb;
- margin: 0;
- padding: 20px;
- }
- .container {
- max-width: 700px;
- margin: 0 auto;
- }
- h1 {
- text-align: center;
- font-size: 24px;
- margin-bottom: 10px;
- }
- .formula {
- font-size: 14px;
- color: #333;
- background: #fff;
- border-radius: 10px;
- padding: 10px;
- margin-bottom: 20px;
- line-height: 1.4;
- box-shadow: 0 2px 5px rgba(0,0,0,0.05);
- }
- .card {
- background: #fff;
- border-radius: 10px;
- padding: 12px 16px;
- margin-bottom: 12px;
- box-shadow: 0 1px 4px rgba(0,0,0,0.08);
- }
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- font-size: 16px;
- margin-bottom: 6px;
- font-weight: bold;
- }
- .weight {
- font-size: 13px;
- color: #666;
- }
- .slider-container {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .slider-container input[type="range"] {
- flex: 1;
- }
- .description {
- font-size: 12px;
- color: #555;
- margin-top: 4px;
- line-height: 1.3;
- }
- .result {
- text-align: center;
- font-size: 20px;
- font-weight: bold;
- margin-top: 20px;
- padding: 12px;
- background: #e0f2fe;
- border-radius: 10px;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <h1>Story Point Estimator</h1>
- <div class="formula">
- <b>Equation:</b><br>
- Weighted Score = ( Σ (Rating × Weight) - Min ) / (Max - Min)<br>
- Effort (E) = 2 ^ (Weighted Score × 5)<br>
- Final Story Points = Closest Fibonacci (1,2,3,5,8,13,21)
- </div>
- <div id="categories"></div>
- <div id="result" class="result">Story Points: 1</div>
- </div>
- <script>
- const categories = [
- {
- id: "technical",
- name: "Technical Complexity",
- weight: 30,
- description: "1 = Simple logic / CRUD\n5 = Complex algorithm, multiple integrations"
- },
- {
- id: "ux",
- name: "UX & Platform Complexity",
- weight: 20,
- description: "1 = Standard UI, 1 platform\n5 = Custom UI, animations, multi-platform"
- },
- {
- id: "quality",
- name: "Quality & Risk Factors",
- weight: 20,
- description: "1 = Clear requirements, no risks\n5 = Unclear, risky, security-critical"
- },
- {
- id: "performance",
- name: "Performance & Scalability",
- weight: 15,
- description: "1 = No constraints\n5 = Realtime, high load, distributed"
- },
- {
- id: "delivery",
- name: "Delivery Effort",
- weight: 15,
- description: "1 = Easy testing & release\n5 = Heavy QA, multiple environments"
- }
- ];
- const fibonacci = [1, 2, 3, 5, 8, 13, 21];
- const container = document.getElementById("categories");
- const resultBox = document.getElementById("result");
- const values = {};
- categories.forEach(cat => {
- values[cat.id] = 1;
- const card = document.createElement("div");
- card.className = "card";
- const header = document.createElement("div");
- header.className = "card-header";
- header.innerHTML = `<span>${cat.name}</span><span class="weight">Weight: ${cat.weight}%</span>`;
- card.appendChild(header);
- const sliderContainer = document.createElement("div");
- sliderContainer.className = "slider-container";
- const slider = document.createElement("input");
- slider.type = "range";
- slider.min = 1;
- slider.max = 5;
- slider.value = 1;
- const valueLabel = document.createElement("span");
- valueLabel.textContent = "1";
- slider.oninput = () => {
- values[cat.id] = parseInt(slider.value, 10);
- valueLabel.textContent = slider.value;
- calculate();
- };
- sliderContainer.appendChild(slider);
- sliderContainer.appendChild(valueLabel);
- card.appendChild(sliderContainer);
- const desc = document.createElement("div");
- desc.className = "description";
- desc.textContent = cat.description;
- card.appendChild(desc);
- container.appendChild(card);
- });
- function calculate() {
- const n = categories.length;
- let weightedSum = 0;
- categories.forEach(cat => {
- weightedSum += values[cat.id] * cat.weight;
- });
- const min = 1 * 100; // all sliders at 1 × weights total (100%)
- const max = 5 * 100; // all sliders at 5 × weights total (100%)
- const normalized = (weightedSum - min) / (max - min);
- const E = Math.pow(2, normalized * 5);
- const closest = fibonacci.reduce((prev, curr) =>
- Math.abs(curr - E) < Math.abs(prev - E) ? curr : prev
- );
- resultBox.textContent = "Story Points: " + closest;
- }
- // Initial
- calculate();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment