JayStormX8's picture
Add 3 files
5cd4384 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Team Performance Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
secondary: '#10B981',
dark: '#1F2937',
light: '#F9FAFB'
}
}
}
}
</script>
<style>
.sidebar {
transition: all 0.3s ease;
}
.modal {
transition: opacity 0.3s ease, transform 0.3s ease;
}
.modal-hidden {
opacity: 0;
transform: translateY(-20px);
pointer-events: none;
}
.kpi-progress {
transition: width 1s ease-in-out;
}
</style>
</head>
<body class="bg-gray-100 font-sans">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div class="sidebar bg-white w-64 md:w-20 lg:w-64 border-r border-gray-200 flex flex-col">
<div class="p-4 border-b border-gray-200 flex items-center justify-between">
<h1 class="text-xl font-bold text-primary md:hidden lg:block">TeamMetrics</h1>
<i class="fas fa-chart-line text-primary text-xl md:block lg:hidden"></i>
<button class="md:hidden text-gray-500" onclick="toggleSidebar()">
<i class="fas fa-bars"></i>
</button>
</div>
<nav class="flex-1 overflow-y-auto p-4">
<ul>
<li class="mb-2">
<a href="#" class="flex items-center p-2 text-primary bg-blue-50 rounded-lg">
<i class="fas fa-home mr-3 md:mr-0 lg:mr-3"></i>
<span class="md:hidden lg:block">Dashboard</span>
</a>
</li>
<li class="mb-2">
<a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg" onclick="showSection('team-section')">
<i class="fas fa-users mr-3 md:mr-0 lg:mr-3"></i>
<span class="md:hidden lg:block">Team Members</span>
</a>
</li>
<li class="mb-2">
<a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg" onclick="showSection('kpi-section')">
<i class="fas fa-bullseye mr-3 md:mr-0 lg:mr-3"></i>
<span class="md:hidden lg:block">KPIs</span>
</a>
</li>
<li class="mb-2">
<a href="#" class="flex items-center p-2 text-gray-600 hover:bg-gray-100 rounded-lg" onclick="showSection('reports-section')">
<i class="fas fa-chart-pie mr-3 md:mr-0 lg:mr-3"></i>
<span class="md:hidden lg:block">Reports</span>
</a>
</li>
</ul>
</nav>
<div class="p-4 border-t border-gray-200">
<div class="flex items-center">
<img src="https://randomuser.me/api/portraits/women/44.jpg" alt="Profile" class="w-8 h-8 rounded-full mr-2">
<div class="md:hidden lg:block">
<p class="text-sm font-medium">Sarah Johnson</p>
<p class="text-xs text-gray-500">Admin</p>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 overflow-auto">
<header class="bg-white shadow-sm p-4 flex justify-between items-center">
<h2 class="text-xl font-semibold text-gray-800" id="section-title">Dashboard</h2>
<div class="flex items-center space-x-4">
<div class="relative">
<input type="text" placeholder="Search..." class="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
</div>
<button class="p-2 text-gray-500 hover:text-gray-700">
<i class="fas fa-bell"></i>
</button>
</div>
</header>
<!-- Dashboard Section -->
<main class="p-4" id="dashboard-section">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div class="bg-white p-6 rounded-lg shadow">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Team Members</p>
<h3 class="text-2xl font-bold" id="team-count">0</h3>
</div>
<div class="bg-blue-100 p-3 rounded-full">
<i class="fas fa-users text-blue-500 text-xl"></i>
</div>
</div>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Active KPIs</p>
<h3 class="text-2xl font-bold" id="kpi-count">0</h3>
</div>
<div class="bg-green-100 p-3 rounded-full">
<i class="fas fa-bullseye text-green-500 text-xl"></i>
</div>
</div>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Avg. Performance</p>
<h3 class="text-2xl font-bold" id="avg-performance">0%</h3>
</div>
<div class="bg-purple-100 p-3 rounded-full">
<i class="fas fa-chart-line text-purple-500 text-xl"></i>
</div>
</div>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500">Top Performer</p>
<h3 class="text-2xl font-bold" id="top-performer">-</h3>
</div>
<div class="bg-yellow-100 p-3 rounded-full">
<i class="fas fa-trophy text-yellow-500 text-xl"></i>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6">
<div class="bg-white p-6 rounded-lg shadow lg:col-span-2">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Performance Overview</h3>
<select class="border border-gray-300 rounded px-3 py-1 text-sm">
<option>Last 7 Days</option>
<option>Last 30 Days</option>
<option>Last Quarter</option>
</select>
</div>
<div class="h-64">
<canvas id="performance-chart"></canvas>
</div>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Top KPIs</h3>
<button class="text-primary text-sm font-medium" onclick="showSection('kpi-section')">View All</button>
</div>
<div id="top-kpis">
<!-- Will be populated by JavaScript -->
</div>
</div>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Recent Activities</h3>
<button class="text-primary text-sm font-medium">View All</button>
</div>
<div class="space-y-4">
<div class="flex items-start">
<div class="bg-blue-100 p-2 rounded-full mr-3">
<i class="fas fa-user-plus text-blue-500"></i>
</div>
<div>
<p class="text-sm"><span class="font-medium">John Doe</span> was added to the team</p>
<p class="text-xs text-gray-500">2 hours ago</p>
</div>
</div>
<div class="flex items-start">
<div class="bg-green-100 p-2 rounded-full mr-3">
<i class="fas fa-bullseye text-green-500"></i>
</div>
<div>
<p class="text-sm">New KPI <span class="font-medium">"Customer Satisfaction"</span> was created</p>
<p class="text-xs text-gray-500">Yesterday</p>
</div>
</div>
<div class="flex items-start">
<div class="bg-purple-100 p-2 rounded-full mr-3">
<i class="fas fa-trophy text-purple-500"></i>
</div>
<div>
<p class="text-sm"><span class="font-medium">Sarah Johnson</span> achieved 95% of her monthly target</p>
<p class="text-xs text-gray-500">3 days ago</p>
</div>
</div>
</div>
</div>
</main>
<!-- Team Members Section -->
<main class="p-4 hidden" id="team-section">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-semibold text-gray-800">Team Members</h2>
<button class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition" onclick="showAddTeamMemberModal()">
<i class="fas fa-plus mr-2"></i> Add Member
</button>
</div>
<div class="bg-white rounded-lg shadow overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Position</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Department</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Performance</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200" id="team-members-table">
<!-- Will be populated by JavaScript -->
</tbody>
</table>
</div>
</div>
</main>
<!-- KPIs Section -->
<main class="p-4 hidden" id="kpi-section">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-semibold text-gray-800">Key Performance Indicators</h2>
<button class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition" onclick="showAddKPIModal()">
<i class="fas fa-plus mr-2"></i> Add KPI
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" id="kpi-cards">
<!-- Will be populated by JavaScript -->
</div>
</main>
<!-- Reports Section -->
<main class="p-4 hidden" id="reports-section">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-semibold text-gray-800">Performance Reports</h2>
<button class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition">
<i class="fas fa-download mr-2"></i> Export
</button>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="mb-6">
<h3 class="text-lg font-semibold mb-2">Generate Report</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Report Type</label>
<select class="w-full border border-gray-300 rounded px-3 py-2">
<option>Team Performance</option>
<option>Individual Performance</option>
<option>KPI Analysis</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Time Period</label>
<select class="w-full border border-gray-300 rounded px-3 py-2">
<option>Last Week</option>
<option>Last Month</option>
<option>Last Quarter</option>
<option>Custom Range</option>
</select>
</div>
<div class="flex items-end">
<button class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition w-full">
Generate Report
</button>
</div>
</div>
</div>
<div class="border-t border-gray-200 pt-6">
<h3 class="text-lg font-semibold mb-4">Recent Reports</h3>
<div class="space-y-4">
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p class="font-medium">Team Performance - Q2 2023</p>
<p class="text-sm text-gray-500">Generated on July 1, 2023</p>
</div>
<div class="flex space-x-2">
<button class="text-blue-500 hover:text-blue-700">
<i class="fas fa-eye"></i>
</button>
<button class="text-green-500 hover:text-green-700">
<i class="fas fa-download"></i>
</button>
</div>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p class="font-medium">Individual Performance - June 2023</p>
<p class="text-sm text-gray-500">Generated on June 30, 2023</p>
</div>
<div class="flex space-x-2">
<button class="text-blue-500 hover:text-blue-700">
<i class="fas fa-eye"></i>
</button>
<button class="text-green-500 hover:text-green-700">
<i class="fas fa-download"></i>
</button>
</div>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div>
<p class="font-medium">KPI Analysis - Q2 2023</p>
<p class="text-sm text-gray-500">Generated on June 15, 2023</p>
</div>
<div class="flex space-x-2">
<button class="text-blue-500 hover:text-blue-700">
<i class="fas fa-eye"></i>
</button>
<button class="text-green-500 hover:text-green-700">
<i class="fas fa-download"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- Add Team Member Modal -->
<div id="team-member-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 modal-hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold" id="team-member-modal-title">Add Team Member</h3>
<button onclick="hideModal('team-member-modal')" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<form id="team-member-form">
<input type="hidden" id="team-member-id">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Full Name</label>
<input type="text" id="team-member-name" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Position</label>
<input type="text" id="team-member-position" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Department</label>
<select id="team-member-department" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
<option value="Sales">Sales</option>
<option value="Marketing">Marketing</option>
<option value="Development">Development</option>
<option value="Support">Support</option>
<option value="HR">Human Resources</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Email</label>
<input type="email" id="team-member-email" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<div class="flex justify-end space-x-3 mt-6">
<button type="button" onclick="hideModal('team-member-modal')" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50">Cancel</button>
<button type="button" onclick="saveTeamMember()" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-blue-600">Save</button>
</div>
</form>
</div>
</div>
</div>
<!-- Add KPI Modal -->
<div id="kpi-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 modal-hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold" id="kpi-modal-title">Add KPI</h3>
<button onclick="hideModal('kpi-modal')" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<form id="kpi-form">
<input type="hidden" id="kpi-id">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">KPI Name</label>
<input type="text" id="kpi-name" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
<textarea id="kpi-description" rows="3" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary"></textarea>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Target Value</label>
<input type="number" id="kpi-target" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Current Value</label>
<input type="number" id="kpi-current" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Frequency</label>
<select id="kpi-frequency" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
<option value="Daily">Daily</option>
<option value="Weekly">Weekly</option>
<option value="Monthly">Monthly</option>
<option value="Quarterly">Quarterly</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Assigned To</label>
<select id="kpi-assigned" class="w-full border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary">
<!-- Will be populated by JavaScript -->
</select>
</div>
<div class="flex justify-end space-x-3 mt-6">
<button type="button" onclick="hideModal('kpi-modal')" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50">Cancel</button>
<button type="button" onclick="saveKPI()" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-blue-600">Save</button>
</div>
</form>
</div>
</div>
</div>
<!-- Confirmation Modal -->
<div id="confirm-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 modal-hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold" id="confirm-modal-title">Confirm Action</h3>
<button onclick="hideModal('confirm-modal')" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<p id="confirm-modal-message">Are you sure you want to perform this action?</p>
<div class="flex justify-end space-x-3 mt-6">
<button type="button" onclick="hideModal('confirm-modal')" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50">Cancel</button>
<button type="button" onclick="confirmAction()" class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600">Confirm</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Data storage
let teamMembers = [
{ id: 1, name: "John Doe", position: "Sales Manager", department: "Sales", email: "[email protected]", performance: 85 },
{ id: 2, name: "Jane Smith", position: "Marketing Specialist", department: "Marketing", email: "[email protected]", performance: 92 },
{ id: 3, name: "Mike Johnson", position: "Developer", department: "Development", email: "[email protected]", performance: 78 },
{ id: 4, name: "Sarah Williams", position: "HR Manager", department: "HR", email: "[email protected]", performance: 88 }
];
let kpis = [
{ id: 1, name: "Sales Revenue", description: "Monthly sales revenue target", target: 100000, current: 85000, frequency: "Monthly", assigned: 1, progress: 85 },
{ id: 2, name: "Customer Satisfaction", description: "Customer satisfaction score", target: 90, current: 87, frequency: "Monthly", assigned: 2, progress: 96.7 },
{ id: 3, name: "Project Completion", description: "Projects completed on time", target: 95, current: 80, frequency: "Quarterly", assigned: 3, progress: 84.2 },
{ id: 4, name: "Employee Retention", description: "Employee retention rate", target: 95, current: 92, frequency: "Quarterly", assigned: 4, progress: 96.8 }
];
// State variables
let currentAction = null;
let currentId = null;
// Initialize the app
document.addEventListener('DOMContentLoaded', function() {
updateDashboard();
renderTeamMembers();
renderKPIs();
populateAssignedDropdown();
setupPerformanceChart();
});
// Section navigation
function showSection(sectionId) {
document.querySelectorAll('main').forEach(section => {
section.classList.add('hidden');
});
document.getElementById(sectionId).classList.remove('hidden');
// Update section title
const titles = {
'team-section': 'Team Members',
'kpi-section': 'Key Performance Indicators',
'reports-section': 'Performance Reports'
};
document.getElementById('section-title').textContent = titles[sectionId] || 'Dashboard';
}
// Modal functions
function showModal(modalId) {
document.getElementById(modalId).classList.remove('modal-hidden');
}
function hideModal(modalId) {
document.getElementById(modalId).classList.add('modal-hidden');
}
function showAddTeamMemberModal() {
document.getElementById('team-member-modal-title').textContent = 'Add Team Member';
document.getElementById('team-member-id').value = '';
document.getElementById('team-member-name').value = '';
document.getElementById('team-member-position').value = '';
document.getElementById('team-member-department').value = 'Sales';
document.getElementById('team-member-email').value = '';
showModal('team-member-modal');
}
function showEditTeamMemberModal(id) {
const member = teamMembers.find(m => m.id === id);
if (member) {
document.getElementById('team-member-modal-title').textContent = 'Edit Team Member';
document.getElementById('team-member-id').value = member.id;
document.getElementById('team-member-name').value = member.name;
document.getElementById('team-member-position').value = member.position;
document.getElementById('team-member-department').value = member.department;
document.getElementById('team-member-email').value = member.email;
showModal('team-member-modal');
}
}
function showAddKPIModal() {
document.getElementById('kpi-modal-title').textContent = 'Add KPI';
document.getElementById('kpi-id').value = '';
document.getElementById('kpi-name').value = '';
document.getElementById('kpi-description').value = '';
document.getElementById('kpi-target').value = '';
document.getElementById('kpi-current').value = '';
document.getElementById('kpi-frequency').value = 'Monthly';
showModal('kpi-modal');
}
function showEditKPIModal(id) {
const kpi = kpis.find(k => k.id === id);
if (kpi) {
document.getElementById('kpi-modal-title').textContent = 'Edit KPI';
document.getElementById('kpi-id').value = kpi.id;
document.getElementById('kpi-name').value = kpi.name;
document.getElementById('kpi-description').value = kpi.description;
document.getElementById('kpi-target').value = kpi.target;
document.getElementById('kpi-current').value = kpi.current;
document.getElementById('kpi-frequency').value = kpi.frequency;
document.getElementById('kpi-assigned').value = kpi.assigned;
showModal('kpi-modal');
}
}
function showConfirmModal(action, id, message) {
currentAction = action;
currentId = id;
document.getElementById('confirm-modal-message').textContent = message || 'Are you sure you want to perform this action?';
showModal('confirm-modal');
}
function confirmAction() {
if (currentAction === 'deleteTeamMember') {
deleteTeamMember(currentId);
} else if (currentAction === 'deleteKPI') {
deleteKPI(currentId);
}
hideModal('confirm-modal');
}
// Team Member functions
function saveTeamMember() {
const id = document.getElementById('team-member-id').value;
const name = document.getElementById('team-member-name').value;
const position = document.getElementById('team-member-position').value;
const department = document.getElementById('team-member-department').value;
const email = document.getElementById('team-member-email').value;
if (!name || !position || !department || !email) {
alert('Please fill in all fields');
return;
}
if (id) {
// Update existing member
const index = teamMembers.findIndex(m => m.id === parseInt(id));
if (index !== -1) {
teamMembers[index] = {
...teamMembers[index],
name,
position,
department,
email
};
}
} else {
// Add new member
const newId = teamMembers.length > 0 ? Math.max(...teamMembers.map(m => m.id)) + 1 : 1;
teamMembers.push({
id: newId,
name,
position,
department,
email,
performance: Math.floor(Math.random() * 30) + 70 // Random performance between 70-100
});
}
hideModal('team-member-modal');
renderTeamMembers();
updateDashboard();
populateAssignedDropdown();
}
function deleteTeamMember(id) {
teamMembers = teamMembers.filter(m => m.id !== id);
// Also remove any KPIs assigned to this member
kpis = kpis.filter(k => k.assigned !== id);
renderTeamMembers();
renderKPIs();
updateDashboard();
populateAssignedDropdown();
}
// KPI functions
function saveKPI() {
const id = document.getElementById('kpi-id').value;
const name = document.getElementById('kpi-name').value;
const description = document.getElementById('kpi-description').value;
const target = parseFloat(document.getElementById('kpi-target').value);
const current = parseFloat(document.getElementById('kpi-current').value);
const frequency = document.getElementById('kpi-frequency').value;
const assigned = parseInt(document.getElementById('kpi-assigned').value);
if (!name || isNaN(target) || isNaN(current) || !frequency || !assigned) {
alert('Please fill in all required fields with valid values');
return;
}
const progress = Math.min(100, (current / target) * 100);
if (id) {
// Update existing KPI
const index = kpis.findIndex(k => k.id === parseInt(id));
if (index !== -1) {
kpis[index] = {
...kpis[index],
name,
description,
target,
current,
frequency,
assigned,
progress
};
}
} else {
// Add new KPI
const newId = kpis.length > 0 ? Math.max(...kpis.map(k => k.id)) + 1 : 1;
kpis.push({
id: newId,
name,
description,
target,
current,
frequency,
assigned,
progress
});
}
hideModal('kpi-modal');
renderKPIs();
updateDashboard();
}
function deleteKPI(id) {
kpis = kpis.filter(k => k.id !== id);
renderKPIs();
updateDashboard();
}
function updateKPIProgress(id, newValue) {
const kpi = kpis.find(k => k.id === id);
if (kpi) {
kpi.current = newValue;
kpi.progress = Math.min(100, (newValue / kpi.target) * 100);
renderKPIs();
updateDashboard();
}
}
// Rendering functions
function renderTeamMembers() {
const tableBody = document.getElementById('team-members-table');
tableBody.innerHTML = '';
teamMembers.forEach(member => {
const row = document.createElement('tr');
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10">
<img class="h-10 w-10 rounded-full" src="https://ui-avatars.com/api/?name=${encodeURIComponent(member.name)}&background=random" alt="">
</div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">${member.name}</div>
<div class="text-sm text-gray-500">${member.email}</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900">${member.position}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">${member.department}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="w-24 mr-2">
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div class="bg-${getPerformanceColor(member.performance)} h-2.5 rounded-full" style="width: ${member.performance}%"></div>
</div>
</div>
<span class="text-sm font-medium text-gray-700">${member.performance}%</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button onclick="showEditTeamMemberModal(${member.id})" class="text-blue-600 hover:text-blue-900 mr-3"><i class="fas fa-edit"></i></button>
<button onclick="showConfirmModal('deleteTeamMember', ${member.id}, 'Are you sure you want to delete ${member.name}?')" class="text-red-600 hover:text-red-900"><i class="fas fa-trash"></i></button>
</td>
`;
tableBody.appendChild(row);
});
}
function renderKPIs() {
const kpiContainer = document.getElementById('kpi-cards');
kpiContainer.innerHTML = '';
kpis.forEach(kpi => {
const assignedMember = teamMembers.find(m => m.id === kpi.assigned);
const card = document.createElement('div');
card.className = 'bg-white rounded-lg shadow overflow-hidden';
card.innerHTML = `
<div class="p-5">
<div class="flex justify-between items-start mb-2">
<h3 class="text-lg font-semibold">${kpi.name}</h3>
<div class="flex space-x-2">
<button onclick="showEditKPIModal(${kpi.id})" class="text-blue-500 hover:text-blue-700"><i class="fas fa-edit"></i></button>
<button onclick="showConfirmModal('deleteKPI', ${kpi.id}, 'Are you sure you want to delete the KPI ${kpi.name}?')" class="text-red-500 hover:text-red-700"><i class="fas fa-trash"></i></button>
</div>
</div>
<p class="text-sm text-gray-600 mb-4">${kpi.description}</p>
<div class="mb-4">
<div class="flex justify-between text-sm mb-1">
<span>Progress</span>
<span>${kpi.progress.toFixed(1)}%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div class="bg-${getPerformanceColor(kpi.progress)} h-2.5 rounded-full kpi-progress" style="width: ${kpi.progress}%"></div>
</div>
</div>
<div class="grid grid-cols-2 gap-4 text-sm mb-4">
<div>
<p class="text-gray-500">Target</p>
<p class="font-medium">${formatNumber(kpi.target)}</p>
</div>
<div>
<p class="text-gray-500">Current</p>
<p class="font-medium">${formatNumber(kpi.current)}</p>
</div>
<div>
<p class="text-gray-500">Frequency</p>
<p class="font-medium">${kpi.frequency}</p>
</div>
<div>
<p class="text-gray-500">Assigned To</p>
<p class="font-medium">${assignedMember ? assignedMember.name : 'Unassigned'}</p>
</div>
</div>
<div class="flex items-center justify-between">
<div class="text-sm">
<p class="text-gray-500">Update Value</p>
</div>
<div class="flex">
<input type="number" id="kpi-update-${kpi.id}" value="${kpi.current}" class="w-20 border border-gray-300 rounded-l px-2 py-1 text-sm">
<button onclick="updateKPIProgress(${kpi.id}, parseFloat(document.getElementById('kpi-update-${kpi.id}').value))" class="bg-primary text-white px-3 py-1 rounded-r text-sm hover:bg-blue-600">Update</button>
</div>
</div>
</div>
`;
kpiContainer.appendChild(card);
});
}
function populateAssignedDropdown() {
const dropdown = document.getElementById('kpi-assigned');
dropdown.innerHTML = '';
teamMembers.forEach(member => {
const option = document.createElement('option');
option.value = member.id;
option.textContent = member.name;
dropdown.appendChild(option);
});
}
// Dashboard functions
function updateDashboard() {
document.getElementById('team-count').textContent = teamMembers.length;
document.getElementById('kpi-count').textContent = kpis.length;
if (teamMembers.length > 0) {
const avgPerformance = teamMembers.reduce((sum, member) => sum + member.performance, 0) / teamMembers.length;
document.getElementById('avg-performance').textContent = avgPerformance.toFixed(1) + '%';
const topPerformer = teamMembers.reduce((top, member) => member.performance > top.performance ? member : top, teamMembers[0]);
document.getElementById('top-performer').textContent = topPerformer.name;
} else {
document.getElementById('avg-performance').textContent = '0%';
document.getElementById('top-performer').textContent = '-';
}
renderTopKPIs();
}
function renderTopKPIs() {
const topKPIsContainer = document.getElementById('top-kpis');
topKPIsContainer.innerHTML = '';
// Sort KPIs by progress (descending) and take top 3
const sortedKPIs = [...kpis].sort((a, b) => b.progress - a.progress).slice(0, 3);
if (sortedKPIs.length === 0) {
topKPIsContainer.innerHTML = '<p class="text-gray-500 text-sm">No KPIs available</p>';
return;
}
sortedKPIs.forEach(kpi => {
const assignedMember = teamMembers.find(m => m.id === kpi.assigned);
const kpiElement = document.createElement('div');
kpiElement.className = 'mb-4';
kpiElement.innerHTML = `
<div class="flex justify-between mb-1">
<span class="text-sm font-medium">${kpi.name}</span>
<span class="text-sm font-medium">${kpi.progress.toFixed(1)}%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-1.5 mb-1">
<div class="bg-${getPerformanceColor(kpi.progress)} h-1.5 rounded-full" style="width: ${kpi.progress}%"></div>
</div>
<div class="flex justify-between text-xs text-gray-500">
<span>${assignedMember ? assignedMember.name : 'Unassigned'}</span>
<span>${kpi.current} of ${kpi.target}</span>
</div>
`;
topKPIsContainer.appendChild(kpiElement);
});
}
function setupPerformanceChart() {
const ctx = document.getElementById('performance-chart').getContext('2d');
// Generate some sample data
const labels = [];
const today = new Date();
for (let i = 6; i >= 0; i--) {
const date = new Date(today);
date.setDate(date.getDate() - i);
labels.push(date.toLocaleDateString('en-US', { weekday: 'short' }));
}
const data = labels.map(() => Math.floor(Math.random() * 30) + 70);
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Team Performance',
data: data,
backgroundColor: 'rgba(59, 130, 246, 0.1)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 2,
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: false,
min: 50,
max: 100
}
}
}
});
}
// Helper functions
function getPerformanceColor(percentage) {
if (percentage >= 90) return 'green-500';
if (percentage >= 75) return 'blue-500';
if (percentage >= 50) return 'yellow-500';
return 'red-500';
}
function formatNumber(num) {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + 'M';
}
if (num >= 1000) {
return (num / 1000).toFixed(1) + 'K';
}
return num.toString();
}
function toggleSidebar() {
const sidebar = document.querySelector('.sidebar');
sidebar.classList.toggle('md:w-20');
sidebar.classList.toggle('lg:w-64');
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=JayStormX8/performance-management-v2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>