moremore / index.html
Tim20121221's picture
Add 3 files
d5b75bf verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Employee Attendance</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #1a3a32;
--secondary-color: #2d5548;
--accent-color: #4caf7d;
--text-light: #f0f9f5;
--text-dark: #1a3a32;
}
body {
background-color: #f5f7f6;
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
}
.month-selector {
scrollbar-width: none;
-ms-overflow-style: none;
}
.month-selector::-webkit-scrollbar {
display: none;
}
.attendance-chart-container {
position: relative;
width: 180px;
height: 180px;
margin: 0 auto;
}
.employee-card {
transition: all 0.2s ease;
}
.employee-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}
.action-btn {
transition: all 0.2s ease;
}
.action-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.selected-month {
background-color: var(--primary-color);
color: white;
box-shadow: 0 2px 8px rgba(26, 58, 50, 0.3);
}
</style>
</head>
<body class="min-h-screen">
<div class="container mx-auto px-4 py-6 max-w-md">
<!-- Header -->
<header class="mb-6">
<h1 class="text-2xl font-bold text-[var(--text-dark)] mb-1">Attendance Dashboard</h1>
<p class="text-sm text-gray-500">Track and manage employee attendance</p>
</header>
<!-- Month Selector -->
<div class="mb-6">
<div class="month-selector flex overflow-x-auto pb-2 gap-2 scroll-smooth" id="month-selector">
<!-- Months will be added here by JavaScript -->
</div>
</div>
<!-- Attendance Rate Card -->
<div class="bg-white rounded-xl shadow-sm p-6 mb-6 border border-gray-100">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold text-[var(--text-dark)]">Attendance Overview</h2>
<div class="text-xs px-2 py-1 rounded-full bg-[var(--primary-color)] text-white">
<span id="selected-month-text">June 2023</span>
</div>
</div>
<div class="flex flex-col md:flex-row items-center">
<div class="attendance-chart-container mb-4 md:mb-0">
<canvas id="attendanceChart"></canvas>
</div>
<div class="text-center md:text-left md:ml-6">
<div class="text-4xl font-bold text-[var(--primary-color)] mb-2" id="attendance-rate">85%</div>
<div class="grid grid-cols-2 gap-4">
<div class="text-left">
<div class="text-sm text-gray-500">Present</div>
<div class="text-xl font-semibold text-[var(--accent-color)]" id="present-days">21</div>
</div>
<div class="text-left">
<div class="text-sm text-gray-500">Absent</div>
<div class="text-xl font-semibold text-red-500" id="absent-days">4</div>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="grid grid-cols-3 gap-3 mb-6">
<button class="action-btn bg-[var(--primary-color)] text-white rounded-xl p-3 flex flex-col items-center">
<i class="fas fa-calendar-alt text-xl mb-2"></i>
<span class="text-xs font-medium">Shift Class</span>
</button>
<button class="action-btn bg-[var(--secondary-color)] text-white rounded-xl p-3 flex flex-col items-center">
<i class="fas fa-cog text-xl mb-2"></i>
<span class="text-xs font-medium">Rules</span>
</button>
<button class="action-btn bg-[var(--accent-color)] text-white rounded-xl p-3 flex flex-col items-center">
<i class="fas fa-user-edit text-xl mb-2"></i>
<span class="text-xs font-medium">Manual</span>
</button>
</div>
<!-- Employee List -->
<div class="bg-white rounded-xl shadow-sm overflow-hidden border border-gray-100">
<div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center">
<h2 class="text-lg font-semibold text-[var(--text-dark)]">Employee Records</h2>
<div class="text-xs text-gray-500">Total: <span id="employee-count">5</span></div>
</div>
<div class="divide-y divide-gray-100" id="employee-list">
<!-- Employee items will be added here by JavaScript -->
</div>
</div>
</div>
<script>
// Set color theme
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#1a3a32',
secondary: '#2d5548',
accent: '#4caf7d',
}
}
}
}
// Generate months data (current month and 5 previous months)
const months = [];
const currentDate = new Date();
for (let i = 5; i >= 0; i--) {
const date = new Date();
date.setMonth(currentDate.getMonth() - i);
months.push({
name: date.toLocaleString('default', { month: 'short' }),
year: date.getFullYear(),
fullName: date.toLocaleString('default', { month: 'long' }) + ' ' + date.getFullYear(),
isCurrent: i === 0
});
}
// Render month selector
const monthSelector = document.getElementById('month-selector');
months.forEach((month, index) => {
const monthElement = document.createElement('div');
monthElement.className = `flex-shrink-0 px-4 py-2 rounded-lg cursor-pointer transition-colors ${month.isCurrent ? 'selected-month' : 'bg-white text-gray-700'}`;
monthElement.innerHTML = `
<div class="text-center">
<div class="font-medium">${month.name}</div>
<div class="text-xs">${month.year}</div>
</div>
`;
monthElement.addEventListener('click', () => {
// Remove selected class from all months
document.querySelectorAll('#month-selector > div').forEach(el => {
el.classList.remove('selected-month');
el.classList.add('bg-white', 'text-gray-700');
});
// Add selected class to clicked month
monthElement.classList.add('selected-month');
monthElement.classList.remove('bg-white', 'text-gray-700');
// Update selected month text
document.getElementById('selected-month-text').textContent = month.fullName;
// In a real app, you would fetch data for the selected month here
updateAttendanceData(month);
});
monthSelector.appendChild(monthElement);
});
// Set initial selected month
document.getElementById('selected-month-text').textContent = months.find(m => m.isCurrent).fullName;
// Sample data - in a real app this would come from an API
function getAttendanceData(month) {
// This is just sample data - in a real app it would vary by month
const basePresent = 20 + Math.floor(Math.random() * 5);
const baseAbsent = 5 - Math.floor(Math.random() * 3);
return {
present: basePresent,
absent: baseAbsent,
rate: Math.round((basePresent / (basePresent + baseAbsent)) * 100)
};
}
// Update attendance display
function updateAttendanceData(month) {
const attendanceData = getAttendanceData(month);
document.getElementById('attendance-rate').textContent = attendanceData.rate + '%';
document.getElementById('present-days').textContent = attendanceData.present;
document.getElementById('absent-days').textContent = attendanceData.absent;
// Update chart
attendanceChart.data.datasets[0].data = [attendanceData.present, attendanceData.absent];
attendanceChart.update();
// Update employee list
updateEmployeeList(month);
}
// Create pie chart
const ctx = document.getElementById('attendanceChart').getContext('2d');
const attendanceChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Present', 'Absent'],
datasets: [{
data: [21, 4], // Initial data
backgroundColor: [
'#4caf7d', // accent color
'#ef4444' // red
],
borderWidth: 0,
cutout: '75%'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
enabled: false
}
},
elements: {
arc: {
borderRadius: 10
}
}
}
});
// Sample employee data
function getEmployeeData(month) {
// This is just sample data - in a real app it would vary by month
return [
{ id: 1, name: 'Zhang San', position: 'Developer', present: 20 + Math.floor(Math.random() * 3), absent: 5 - Math.floor(Math.random() * 2), avatar: 'https://randomuser.me/api/portraits/men/1.jpg' },
{ id: 2, name: 'Li Si', position: 'Designer', present: 22 + Math.floor(Math.random() * 2), absent: 3 - Math.floor(Math.random() * 1), avatar: 'https://randomuser.me/api/portraits/women/2.jpg' },
{ id: 3, name: 'Wang Wu', position: 'Manager', present: 18 + Math.floor(Math.random() * 4), absent: 7 - Math.floor(Math.random() * 3), avatar: 'https://randomuser.me/api/portraits/men/3.jpg' },
{ id: 4, name: 'Zhao Liu', position: 'Marketing', present: 23 + Math.floor(Math.random() * 2), absent: 2 - Math.floor(Math.random() * 1), avatar: 'https://randomuser.me/api/portraits/women/4.jpg' },
{ id: 5, name: 'Qian Qi', position: 'HR', present: 21 + Math.floor(Math.random() * 3), absent: 4 - Math.floor(Math.random() * 2), avatar: 'https://randomuser.me/api/portraits/men/5.jpg' }
];
}
// Render employee list
function updateEmployeeList(month) {
const employeeList = document.getElementById('employee-list');
employeeList.innerHTML = '';
const employees = getEmployeeData(month);
document.getElementById('employee-count').textContent = employees.length;
employees.forEach(employee => {
const rate = Math.round((employee.present / (employee.present + employee.absent)) * 100);
const employeeItem = document.createElement('div');
employeeItem.className = 'employee-card px-4 py-3';
employeeItem.innerHTML = `
<div class="flex items-center">
<div class="relative mr-3">
<img src="${employee.avatar}" alt="${employee.name}" class="w-10 h-10 rounded-full object-cover">
<div class="absolute -bottom-1 -right-1 w-4 h-4 rounded-full border-2 border-white ${rate >= 90 ? 'bg-green-500' : rate >= 70 ? 'bg-yellow-500' : 'bg-red-500'}"></div>
</div>
<div class="flex-1 min-w-0">
<h3 class="font-medium text-[var(--text-dark)] truncate">${employee.name}</h3>
<p class="text-xs text-gray-500 truncate">${employee.position}</p>
</div>
<div class="flex items-center space-x-3">
<div class="text-right">
<div class="text-sm font-medium ${rate >= 90 ? 'text-green-600' : rate >= 70 ? 'text-yellow-600' : 'text-red-600'}">
${rate}%
</div>
<div class="text-xs text-gray-500 flex space-x-1">
<span class="text-green-500">${employee.present}P</span>
<span>/</span>
<span class="text-red-500">${employee.absent}A</span>
</div>
</div>
<i class="fas fa-chevron-right text-gray-400 text-xs"></i>
</div>
</div>
`;
employeeList.appendChild(employeeItem);
});
}
// Initialize with current month data
updateAttendanceData(months.find(m => m.isCurrent));
// Add click handlers for action buttons
document.querySelectorAll('.action-btn').forEach((btn, index) => {
btn.addEventListener('click', () => {
const actions = ['Shift Setup', 'Rules Setup', 'Manual Attendance'];
alert(`${actions[index]} clicked - this would open the ${actions[index]} screen`);
});
});
</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=Tim20121221/moremore" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>