Ngiler SH3LL 360
Home
Information
Create File
Create Folder
:
/
home
/
tbf
/
responsabilitati.tbf.ro
/
Information Server
MySQL :
OFF
Perl :
OFF
CURL :
ON
WGET :
OFF
PKEXEC :
OFF
Directive
Local Value
IP Address
89.40.16.97
System
Linux server.atelieruldeit.ro 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
User
tbf
PHP Version
7.3.33
Software
Apache
Doc root
Writable
close
Edit File :
index.html
| Size :
27.34
KB
Copy
<!DOCTYPE html> <html lang="ro"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Calculator Punctaje Responsabilități Rol</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 15px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); overflow: hidden; } .header { background: linear-gradient(135deg, #4a69bd 0%, #6c5ce7 100%); color: white; padding: 30px; text-align: center; } .header h1 { font-size: 2.5em; margin-bottom: 20px; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); } .role-config { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px; margin-bottom: 20px; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 15px; margin-top: 20px; } .stat-item { background: rgba(255, 255, 255, 0.2); padding: 10px; border-radius: 8px; text-align: center; backdrop-filter: blur(10px); } .stat-item .value { font-size: 18px; font-weight: bold; color: white; margin-bottom: 3px; } .stat-item .label { font-size: 10px; color: rgba(255, 255, 255, 0.8); text-transform: uppercase; letter-spacing: 1px; } .config-group { display: flex; flex-direction: column; } .config-group label { font-weight: 600; margin-bottom: 8px; font-size: 1.1em; } .config-group input { padding: 12px; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 8px; background: rgba(255, 255, 255, 0.1); color: white; font-size: 16px; } .config-group input::placeholder { color: rgba(255, 255, 255, 0.7); } .main-content { padding: 30px; } .responsibilities-section { margin-bottom: 30px; } .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .section-header h2 { color: #2d3436; font-size: 1.8em; } .add-btn { background: linear-gradient(135deg, #00b894 0%, #00a085 100%); color: white; border: none; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: 600; transition: transform 0.2s; } .add-btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 184, 148, 0.4); } .responsibility-card { background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 12px; padding: 20px; margin-bottom: 15px; transition: all 0.3s ease; position: relative; } .responsibility-card:hover { box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1); transform: translateY(-2px); } .responsibility-row { display: grid; grid-template-columns: 200px 120px 1fr 1fr 1fr 1fr 100px; gap: 20px; align-items: center; } .name-input { padding: 10px; border: 2px solid #ddd; border-radius: 6px; font-size: 14px; transition: border-color 0.3s; } .name-input:focus { outline: none; border-color: #6c5ce7; } .card-score { background: linear-gradient(135deg, #fd79a8 0%, #e84393 100%); color: white; padding: 15px 10px; border-radius: 10px; text-align: center; } .score-value { font-size: 20px; font-weight: bold; margin-bottom: 3px; } .score-label { font-size: 10px; opacity: 0.9; } .slider-group { text-align: center; } .slider-group label { display: block; color: #2d3436; font-weight: 600; margin-bottom: 8px; font-size: 12px; } .slider-container { position: relative; margin: 5px 0; } .slider { width: 100%; height: 5px; border-radius: 3px; background: #ddd; outline: none; -webkit-appearance: none; appearance: none; } .slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 18px; height: 18px; border-radius: 50%; background: #6c5ce7; cursor: pointer; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); } .slider::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: #6c5ce7; cursor: pointer; border: none; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); } .slider-value { margin-top: 5px; font-weight: bold; color: #6c5ce7; font-size: 11px; } .remove-btn { position: absolute; top: 15px; right: 15px; background: #e74c3c; color: white; border: none; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-size: 16px; font-weight: bold; transition: transform 0.2s; } .remove-btn:hover { transform: scale(1.1); background: #c0392b; } @media (max-width: 768px) { .role-config { grid-template-columns: 1fr; } .responsibility-row { grid-template-columns: 1fr; gap: 15px; text-align: center; } .stats-grid { grid-template-columns: repeat(2, 1fr); } .hours-input-group { justify-content: center; } .impact-buttons { justify-content: center; } .header h1 { font-size: 2em; } .main-content { padding: 20px; } } </style> </head> <body> <div class="container"> <header class="header"> <h1>Calculator Punctaje Responsabilități Rol</h1> <div class="role-config"> <div class="config-group"> <label for="roleName">Numele Rolului:</label> <input type="text" id="roleName" placeholder="Ex: Manager Vânzări" value="Manager Vânzări"> </div> <div class="config-group"> <label for="totalHours">Total Ore Lunar:</label> <input type="number" id="totalHours" min="1" max="400" value="160"> </div> </div> <div class="stats-grid" id="statsGrid"> <!-- Statisticile vor fi generate dinamic aici --> </div> </header> <main class="main-content"> <section class="responsibilities-section"> <div class="section-header"> <h2>Responsabilități</h2> <button class="add-btn" onclick="addResponsibility()">+ Adaugă Responsabilitate</button> </div> <div id="responsibilitiesContainer"> <!-- Responsabilitățile vor fi adăugate dinamic aici --> </div> </section> </main> </div> <script> let responsibilities = []; let responsibilityCounter = 0; // Responsabilități presetate const presetResponsibilities = [ { name: "Planning", hours: 40, revenue: 4, costs: 3, experience: 3 }, { name: "Team Management", hours: 35, revenue: 3, costs: 4, experience: 4 }, { name: "Sales Activities", hours: 50, revenue: 5, costs: 2, experience: 5 }, { name: "Reporting", hours: 20, revenue: 2, costs: 3, experience: 2 }, { name: "Training", hours: 15, revenue: 3, costs: 4, experience: 4 } ]; function initializeApp() { // Adaugă responsabilitățile presetate presetResponsibilities.forEach(preset => { addResponsibility(preset); }); // Adaugă event listeners pentru configurația rolului document.getElementById('roleName').addEventListener('input', updateAllScores); document.getElementById('totalHours').addEventListener('input', () => { // Actualizează afișarea procentajelor pentru toate responsabilitățile responsibilities.forEach(resp => { updateHoursDisplay(resp.id); }); updateSliderLimits(); updateAllScores(); }); updateSliderLimits(); updateAllScores(); } function addResponsibility(preset = null) { const id = ++responsibilityCounter; const responsibility = { id: id, name: preset ? preset.name : '', hours: preset ? preset.hours : 0, revenue: preset ? preset.revenue : 0, costs: preset ? preset.costs : 0, experience: preset ? preset.experience : 0 }; responsibilities.push(responsibility); renderResponsibility(responsibility); // Actualizează afișarea orelor pentru noua responsabilitate updateHoursDisplay(id); updateSliderLimits(); updateAllScores(); } function renderResponsibility(responsibility) { const container = document.getElementById('responsibilitiesContainer'); const card = document.createElement('div'); card.className = 'responsibility-card'; card.id = `responsibility-${responsibility.id}`; card.innerHTML = ` <button class="remove-btn" onclick="removeResponsibility(${responsibility.id})">×</button> <div class="responsibility-row"> <div> <label style="font-size: 12px; color: #2d3436; font-weight: 600; margin-bottom: 5px; display: block;">Nume Responsabilitate</label> <input type="text" class="name-input" id="name-${responsibility.id}" value="${responsibility.name}" placeholder="Ex: Planning, Management..." onchange="updateResponsibility(${responsibility.id})"> </div> <div class="card-score"> <div class="score-value" id="score-${responsibility.id}">0</div> <div class="score-label">PUNCTE</div> </div> <div class="hours-control"> <label>Ore/Lună</label> <div class="hours-input-group"> <button class="hours-btn" id="hours-minus-${responsibility.id}" onclick="changeHours(${responsibility.id}, -1)">−</button> <input type="number" class="hours-input" id="hours-${responsibility.id}" value="${responsibility.hours}" min="0" max="200" onchange="updateResponsibility(${responsibility.id})"> <button class="hours-btn" id="hours-plus-${responsibility.id}" onclick="changeHours(${responsibility.id}, 1)">+</button> </div> <div class="hours-value" id="hours-value-${responsibility.id}">${responsibility.hours} ore</div> </div> <div class="impact-control"> <label>Impact Venituri</label> <div class="impact-buttons" id="revenue-buttons-${responsibility.id}"> ${generateImpactButtons(responsibility.id, 'revenue', responsibility.revenue)} </div> <div class="impact-value" id="revenue-value-${responsibility.id}">${responsibility.revenue}/5</div> </div> <div class="impact-control"> <label>Impact Costuri</label> <div class="impact-buttons" id="costs-buttons-${responsibility.id}"> ${generateImpactButtons(responsibility.id, 'costs', responsibility.costs)} </div> <div class="impact-value" id="costs-value-${responsibility.id}">${responsibility.costs}/5</div> </div> <div class="impact-control"> <label>Exp. Client</label> <div class="impact-buttons" id="experience-buttons-${responsibility.id}"> ${generateImpactButtons(responsibility.id, 'experience', responsibility.experience)} </div> <div class="impact-value" id="experience-value-${responsibility.id}">${responsibility.experience}/5</div> </div> </div> `; container.appendChild(card); } function generateImpactButtons(responsibilityId, type, currentValue) { let buttons = ''; for (let i = 0; i <= 5; i++) { const activeClass = i === currentValue ? 'active' : ''; buttons += `<button class="impact-btn ${activeClass}" onclick="setImpact(${responsibilityId}, '${type}', ${i})">${i}</button>`; } return buttons; } function changeHours(id, delta) { const responsibility = responsibilities.find(r => r.id === id); if (!responsibility) return; const currentHours = responsibility.hours; const newHours = Math.max(0, currentHours + delta); // Verifică dacă noua valoare respectă limitele const totalHours = parseInt(document.getElementById('totalHours').value) || 160; const otherHours = responsibilities .filter(r => r.id !== id) .reduce((sum, r) => sum + r.hours, 0); const maxAllowed = totalHours - otherHours; if (newHours <= Math.min(200, maxAllowed)) { responsibility.hours = newHours; document.getElementById(`hours-${id}`).value = newHours; updateHoursDisplay(id); updateSliderLimits(); updateAllScores(); } } function setImpact(id, type, value) { const responsibility = responsibilities.find(r => r.id === id); if (!responsibility) return; responsibility[type] = value; // Actualizează butoanele vizual - îndepărtează toate clasele active și adaugă doar la cel selectat const buttonsContainer = document.getElementById(`${type}-buttons-${id}`); if (buttonsContainer) { const buttons = buttonsContainer.querySelectorAll('.impact-btn'); buttons.forEach((btn, index) => { btn.classList.remove('active'); if (index === value) { btn.classList.add('active'); } }); } // Actualizează afișarea valorii const valueElement = document.getElementById(`${type}-value-${id}`); if (valueElement) { valueElement.textContent = `${value}/5`; } updateAllScores(); } function updateHoursDisplay(id) { const responsibility = responsibilities.find(r => r.id === id); if (!responsibility) return; const totalHours = parseInt(document.getElementById('totalHours').value) || 160; const hours = responsibility.hours; const percentage = totalHours > 0 ? ((hours / totalHours) * 100).toFixed(1) : 0; document.getElementById(`hours-value-${id}`).textContent = `${hours} ore (${percentage}%)`; } function updateSliderLimits() { const totalHours = parseInt(document.getElementById('totalHours').value) || 160; responsibilities.forEach(responsibility => { // Calculează orele alocate de alte responsabilități const otherHours = responsibilities .filter(r => r.id !== responsibility.id) .reduce((sum, r) => sum + r.hours, 0); // Orele disponibile pentru această responsabilitate const availableHours = totalHours - otherHours; const maxHours = Math.max(0, Math.min(200, availableHours)); const input = document.getElementById(`hours-${responsibility.id}`); const plusBtn = document.getElementById(`hours-plus-${responsibility.id}`); const minusBtn = document.getElementById(`hours-minus-${responsibility.id}`); if (input && plusBtn && minusBtn) { input.max = maxHours; // Disable/enable butoanele plusBtn.disabled = responsibility.hours >= maxHours; minusBtn.disabled = responsibility.hours <= 0; // Dacă valoarea curentă depășește noul max, o reducem if (responsibility.hours > maxHours) { responsibility.hours = maxHours; input.value = maxHours; updateHoursDisplay(responsibility.id); } } }); } function updateResponsibility(id) { const responsibility = responsibilities.find(r => r.id === id); if (!responsibility) return; responsibility.name = document.getElementById(`name-${id}`).value; // Pentru input-ul de ore const hoursInput = document.getElementById(`hours-${id}`); if (hoursInput) { const newHours = parseInt(hoursInput.value) || 0; const totalHours = parseInt(document.getElementById('totalHours').value) || 160; const otherHours = responsibilities .filter(r => r.id !== id) .reduce((sum, r) => sum + r.hours, 0); const maxAllowed = Math.min(200, totalHours - otherHours); responsibility.hours = Math.max(0, Math.min(newHours, maxAllowed)); hoursInput.value = responsibility.hours; updateHoursDisplay(id); } // Actualizează limitele sliderelor după modificare updateSliderLimits(); updateAllScores(); } function removeResponsibility(id) { responsibilities = responsibilities.filter(r => r.id !== id); const card = document.getElementById(`responsibility-${id}`); if (card) { card.remove(); } updateSliderLimits(); updateAllScores(); } function calculateScores() { const totalHours = parseInt(document.getElementById('totalHours').value) || 1; if (responsibilities.length === 0) return []; // Calculează P_timp și P_impact pentru fiecare responsabilitate let totalImpact = 0; const calculations = responsibilities.map(resp => { const pTime = resp.hours / totalHours; const pImpact = resp.revenue + resp.costs + resp.experience; totalImpact += pImpact; return { id: resp.id, pTime: pTime, pImpact: pImpact, name: resp.name || 'Fără nume' }; }); // Evită împărțirea la zero if (totalImpact === 0) totalImpact = 1; // Calculează P_importanță și scorul brut pentru fiecare let totalRawScore = 0; const scores = calculations.map(calc => { const pImportance = calc.pImpact / totalImpact; const rawScore = ((calc.pTime + pImportance) / 2) * 100; totalRawScore += rawScore; return { id: calc.id, name: calc.name, rawScore: rawScore, pTime: calc.pTime, pImportance: pImportance }; }); // Normalizează scorurile să sumeze 100 if (totalRawScore === 0) totalRawScore = 1; const normalizedScores = scores.map(score => ({ ...score, exactScore: (score.rawScore / totalRawScore) * 100 })); // Rotunjire inteligentă care garantează suma de 100 return roundToSum100(normalizedScores); } function roundToSum100(scores) { // Calculează partea întreagă și partea fracționară pentru fiecare scor const processed = scores.map(score => ({ ...score, floorValue: Math.floor(score.exactScore), remainder: score.exactScore - Math.floor(score.exactScore) })); // Calculează suma părților întregi const sumFloors = processed.reduce((sum, item) => sum + item.floorValue, 0); // Calculează câte puncte trebuie să adăugăm pentru a ajunge la 100 const pointsToAdd = 100 - sumFloors; // Sortează după partea fracționară descrescător pentru a decide cărora să le adăugăm +1 const sortedByRemainder = [...processed].sort((a, b) => b.remainder - a.remainder); // Adaugă +1 la primele 'pointsToAdd' elemente for (let i = 0; i < Math.min(pointsToAdd, sortedByRemainder.length); i++) { sortedByRemainder[i].floorValue += 1; } // Returnează în ordinea originală cu scorurile rotunjite return scores.map(originalScore => { const processedItem = sortedByRemainder.find(item => item.id === originalScore.id); return { ...originalScore, normalizedScore: processedItem.floorValue }; }); } function updateAllScores() { const scores = calculateScores(); let totalScore = 0; let totalHours = 0; let avgRevenue = 0; let avgCosts = 0; let avgExperience = 0; scores.forEach(score => { const scoreElement = document.getElementById(`score-${score.id}`); if (scoreElement) { scoreElement.textContent = score.normalizedScore; // Afișează numărul întreg } totalScore += score.normalizedScore; }); // Calculează statistici pentru sumar if (responsibilities.length > 0) { totalHours = responsibilities.reduce((sum, resp) => sum + resp.hours, 0); avgRevenue = responsibilities.reduce((sum, resp) => sum + resp.revenue, 0) / responsibilities.length; avgCosts = responsibilities.reduce((sum, resp) => sum + resp.costs, 0) / responsibilities.length; avgExperience = responsibilities.reduce((sum, resp) => sum + resp.experience, 0) / responsibilities.length; } updateSummary(totalScore, totalHours, avgRevenue, avgCosts, avgExperience); } function updateSummary(totalScore, totalHours, avgRevenue, avgCosts, avgExperience) { const statsGrid = document.getElementById('statsGrid'); const configuredHours = parseInt(document.getElementById('totalHours').value) || 160; statsGrid.innerHTML = ` <div class="stat-item"> <div class="value">${Math.round(totalScore)}</div> <div class="label">Total Puncte</div> </div> <div class="stat-item"> <div class="value">${responsibilities.length}</div> <div class="label">Responsabilități</div> </div> <div class="stat-item"> <div class="value">${totalHours}</div> <div class="label">Ore Alocate</div> </div> <div class="stat-item"> <div class="value">${configuredHours}</div> <div class="label">Ore Disponibile</div> </div> <div class="stat-item"> <div class="value">${avgRevenue.toFixed(1)}</div> <div class="label">Avg. Venituri</div> </div> <div class="stat-item"> <div class="value">${avgCosts.toFixed(1)}</div> <div class="label">Avg. Costuri</div> </div> <div class="stat-item"> <div class="value">${avgExperience.toFixed(1)}</div> <div class="label">Avg. Client</div> </div> <div class="stat-item"> <div class="value">${totalHours > configuredHours ? 'NU' : 'DA'}</div> <div class="label">În Limită</div> </div> `; } // Inițializează aplicația când se încarcă pagina window.onload = initializeApp; </script> </body> </html>
Back