undefined / index.html
sverdlov's picture
still not functional. Clicking 'new department' does nothing, import does nothing, can't create new departments or employees within a department, this is all only UI not an actual app
3ce310d verified
Raw
History Blame Contribute Delete
35.9 kB
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TaskSphere Dashboard</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#4F46E5',
secondary: '#10B981',
}
}
}
}
</script>
<style>
.pie-chart:hover {
transform: scale(1.05);
transition: transform 0.3s ease;
cursor: pointer;
}
.vanta-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.1;
}
</style>
</head>
<body class="min-h-full bg-gray-50">
<div id="vanta-globe" class="vanta-canvas"></div>
<!-- Navigation -->
<nav class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex">
<div class="flex-shrink-0 flex items-center">
<i data-feather="compass" class="h-8 w-8 text-primary"></i>
<span class="ml-2 text-xl font-bold text-gray-900">TaskSphere</span>
</div>
<div class="hidden sm:ml-6 sm:flex sm:space-x-8">
<a href="index.html" class="border-primary text-gray-900 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">Dashboard</a>
<a href="department.html" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">Departments</a>
<a href="tasks.html" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">Tasks</a>
<a href="reports.html" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">Reports</a>
</div>
</div>
<div class="hidden sm:ml-6 sm:flex sm:items-center">
<button type="button" class="bg-white p-1 rounded-full text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
<i data-feather="bell" class="h-6 w-6"></i>
</button>
<div class="ml-3 relative">
<div>
<button type="button" class="bg-white rounded-full flex text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary" id="user-menu" aria-expanded="false" aria-haspopup="true">
<span class="sr-only">Open user menu</span>
<img class="h-8 w-8 rounded-full" src="http://static.photos/office/200x200/1" alt="">
</button>
</div>
</div>
</div>
<div class="-mr-2 flex items-center sm:hidden">
<button type="button" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary" aria-controls="mobile-menu" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<i data-feather="menu" class="block h-6 w-6"></i>
<i data-feather="x" class="hidden h-6 w-6"></i>
</button>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<div class="py-10">
<header>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold leading-tight text-gray-900">Corporate Dashboard</h1>
<div class="mt-4 flex items-center">
<div class="relative rounded-md shadow-sm w-64">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i data-feather="search" class="h-5 w-5 text-gray-400"></i>
</div>
<input type="text" class="focus:ring-primary focus:border-primary block w-full pl-10 sm:text-sm border-gray-300 rounded-md py-2" placeholder="Search departments...">
</div>
<div class="ml-4">
<button type="button" onclick="openNewDepartmentModal()" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-primary hover:bg-primary-dark focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
<i data-feather="plus" class="-ml-1 mr-2 h-5 w-5"></i>
New Department
</button>
</div>
<div class="ml-4">
<button type="button" onclick="openImportModal()" class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
<i data-feather="upload" class="-ml-1 mr-2 h-5 w-5 text-gray-500"></i>
Import
</button>
</div>
</div>
</div>
</header>
<main>
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="px-4 py-8 sm:px-0">
<!-- Stats -->
<div class="grid grid-cols-1 gap-5 sm:grid-cols-3">
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="px-4 py-5 sm:p-6">
<div class="flex items-center">
<div class="flex-shrink-0 bg-primary bg-opacity-10 rounded-md p-3">
<i data-feather="users" class="h-6 w-6 text-primary"></i>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">Total Departments</dt>
<dd>
<div class="text-lg font-medium text-gray-900">12</div>
</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="px-4 py-5 sm:p-6">
<div class="flex items-center">
<div class="flex-shrink-0 bg-secondary bg-opacity-10 rounded-md p-3">
<i data-feather="user" class="h-6 w-6 text-secondary"></i>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">Total Employees</dt>
<dd>
<div class="text-lg font-medium text-gray-900">247</div>
</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="px-4 py-5 sm:p-6">
<div class="flex items-center">
<div class="flex-shrink-0 bg-purple-100 rounded-md p-3">
<i data-feather="check-circle" class="h-6 w-6 text-purple-600"></i>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">Tasks Completed</dt>
<dd>
<div class="text-lg font-medium text-gray-900">1,248</div>
</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
<!-- Department Performance -->
<div class="mt-8">
<h2 class="text-lg leading-6 font-medium text-gray-900">Department Performance</h2>
<div class="mt-4 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
<!-- Marketing Department -->
<div class="bg-white overflow-hidden shadow rounded-lg pie-chart" onclick="window.location.href='/department/marketing'">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">Marketing</h3>
<div class="mt-2 flex justify-between items-baseline">
<p class="text-sm text-gray-500"><span class="font-semibold text-secondary">68%</span> tasks completed</p>
</div>
<div class="mt-4" id="marketing-chart"></div>
</div>
</div>
<!-- Development Department -->
<div class="bg-white overflow-hidden shadow rounded-lg pie-chart" onclick="window.location.href='/department/development'">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">Development</h3>
<div class="mt-2 flex justify-between items-baseline">
<p class="text-sm text-gray-500"><span class="font-semibold text-secondary">82%</span> tasks completed</p>
</div>
<div class="mt-4" id="development-chart"></div>
</div>
</div>
<!-- Sales Department -->
<div class="bg-white overflow-hidden shadow rounded-lg pie-chart" onclick="window.location.href='/department/sales'">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">Sales</h3>
<div class="mt-2 flex justify-between items-baseline">
<p class="text-sm text-gray-500"><span class="font-semibold text-secondary">54%</span> tasks completed</p>
</div>
<div class="mt-4" id="sales-chart"></div>
</div>
</div>
<!-- HR Department -->
<div class="bg-white overflow-hidden shadow rounded-lg pie-chart" onclick="window.location.href='/department/hr'">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">Human Resources</h3>
<div class="mt-2 flex justify-between items-baseline">
<p class="text-sm text-gray-500"><span class="font-semibold text-secondary">91%</span> tasks completed</p>
</div>
<div class="mt-4" id="hr-chart"></div>
</div>
</div>
<!-- Finance Department -->
<div class="bg-white overflow-hidden shadow rounded-lg pie-chart" onclick="window.location.href='/department/finance'">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">Finance</h3>
<div class="mt-2 flex justify-between items-baseline">
<p class="text-sm text-gray-500"><span class="font-semibold text-secondary">76%</span> tasks completed</p>
</div>
<div class="mt-4" id="finance-chart"></div>
</div>
</div>
<!-- Operations Department -->
<div class="bg-white overflow-hidden shadow rounded-lg pie-chart" onclick="window.location.href='/department/operations'">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">Operations</h3>
<div class="mt-2 flex justify-between items-baseline">
<p class="text-sm text-gray-500"><span class="font-semibold text-secondary">63%</span> tasks completed</p>
</div>
<div class="mt-4" id="operations-chart"></div>
</div>
</div>
</div>
</div>
<!-- Recent Tasks -->
<div class="mt-8">
<h2 class="text-lg leading-6 font-medium text-gray-900">Recent Tasks</h2>
<div class="mt-4 bg-white shadow overflow-hidden sm:rounded-md">
<ul class="divide-y divide-gray-200">
<li>
<div class="px-4 py-4 sm:px-6">
<div class="flex items-center justify-between">
<p class="text-sm font-medium text-primary truncate">Launch new marketing campaign</p>
<div class="ml-2 flex-shrink-0 flex">
<p class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Completed</p>
</div>
</div>
<div class="mt-2 sm:flex sm:justify-between">
<div class="sm:flex">
<p class="flex items-center text-sm text-gray-500">
<i data-feather="users" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
Marketing
</p>
<p class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0 sm:ml-6">
<i data-feather="calendar" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
Due May 15, 2023
</p>
</div>
<div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0">
<i data-feather="clock" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
<p>Completed <time datetime="2023-05-12">May 12, 2023</time></p>
</div>
</div>
</div>
</li>
<li>
<div class="px-4 py-4 sm:px-6">
<div class="flex items-center justify-between">
<p class="text-sm font-medium text-primary truncate">Implement new API endpoints</p>
<div class="ml-2 flex-shrink-0 flex">
<p class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-secondary-100 text-secondary-800">In Progress</p>
</div>
</div>
<div class="mt-2 sm:flex sm:justify-between">
<div class="sm:flex">
<p class="flex items-center text-sm text-gray-500">
<i data-feather="users" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
Development
</p>
<p class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0 sm:ml-6">
<i data-feather="calendar" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
Due May 20, 2023
</p>
</div>
<div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0">
<i data-feather="clock" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
<p>Started <time datetime="2023-05-10">May 10, 2023</time></p>
</div>
</div>
</div>
</li>
<li>
<div class="px-4 py-4 sm:px-6">
<div class="flex items-center justify-between">
<p class="text-sm font-medium text-primary truncate">Hire new sales representatives</p>
<div class="ml-2 flex-shrink-0 flex">
<p class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">Pending</p>
</div>
</div>
<div class="mt-2 sm:flex sm:justify-between">
<div class="sm:flex">
<p class="flex items-center text-sm text-gray-500">
<i data-feather="users" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
Human Resources
</p>
<p class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0 sm:ml-6">
<i data-feather="calendar" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
Due June 1, 2023
</p>
</div>
<div class="mt-2 flex items-center text-sm text-gray-500 sm:mt-0">
<i data-feather="clock" class="flex-shrink-0 mr-1.5 h-5 w-5 text-gray-400"></i>
<p>Created <time datetime="2023-05-05">May 5, 2023</time></p>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- New Department Modal -->
<div id="newDepartmentModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div class="mt-3 text-center">
<h3 class="text-lg leading-6 font-medium text-gray-900">Create New Department</h3>
<div class="mt-2 px-7 py-3">
<div class="mb-4">
<label for="deptName" class="block text-sm font-medium text-gray-700 text-left">Department Name</label>
<input type="text" id="deptName" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm">
</div>
<div class="mb-4">
<label for="deptDescription" class="block text-sm font-medium text-gray-700 text-left">Description</label>
<textarea id="deptDescription" rows="3" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm"></textarea>
</div>
<div class="mb-4">
<label for="deptManager" class="block text-sm font-medium text-gray-700 text-left">Manager</label>
<select id="deptManager" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary focus:border-primary sm:text-sm">
<option value="">Select a manager</option>
</select>
</div>
</div>
<div class="items-center px-4 py-3">
<button id="createDeptBtn" onclick="createDepartment()" class="px-4 py-2 bg-primary text-white text-base font-medium rounded-md shadow-sm hover:bg-primary-dark focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
Create
</button>
<button onclick="closeModal('newDepartmentModal')" class="ml-3 px-4 py-2 bg-gray-200 text-gray-700 text-base font-medium rounded-md shadow-sm hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
Cancel
</button>
</div>
</div>
</div>
</div>
<!-- Import Modal -->
<div id="importModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div class="mt-3 text-center">
<h3 class="text-lg leading-6 font-medium text-gray-900">Import Departments</h3>
<div class="mt-2 px-7 py-3">
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 text-left">File</label>
<div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
<div class="space-y-1 text-center">
<div class="flex text-sm text-gray-600">
<label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-medium text-primary hover:text-primary-dark focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-primary">
<span>Upload a file</span>
<input id="file-upload" name="file-upload" type="file" class="sr-only">
</label>
<p class="pl-1">or drag and drop</p>
</div>
<p class="text-xs text-gray-500">
CSV files only
</p>
</div>
</div>
</div>
</div>
<div class="items-center px-4 py-3">
<button onclick="importDepartments()" class="px-4 py-2 bg-primary text-white text-base font-medium rounded-md shadow-sm hover:bg-primary-dark focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">
Import
</button>
<button onclick="closeModal('importModal')" class="ml-3 px-4 py-2 bg-gray-200 text-gray-700 text-base font-medium rounded-md shadow-sm hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
Cancel
</button>
</div>
</div>
</div>
</div>
<script>
// API Base URL
const API_BASE_URL = 'http://localhost:5000/api';
let authToken = localStorage.getItem('authToken');
// Initialize app when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
if (!authToken) {
// Redirect to login if not authenticated
window.location.href = '/login.html';
return;
}
loadManagers();
loadDepartments();
loadRecentTasks();
});
// Modal functions
function openNewDepartmentModal() {
document.getElementById('newDepartmentModal').classList.remove('hidden');
}
function openImportModal() {
document.getElementById('importModal').classList.remove('hidden');
}
function closeModal(modalId) {
document.getElementById(modalId).classList.add('hidden');
}
// Load managers for dropdown
async function loadManagers() {
try {
const response = await fetch(`${API_BASE_URL}/users`, {
headers: {
'x-auth-token': authToken
}
});
const users = await response.json();
const managerSelect = document.getElementById('deptManager');
users.forEach(user => {
if (user.role === 'manager') {
const option = document.createElement('option');
option.value = user._id;
option.textContent = user.name;
managerSelect.appendChild(option);
}
});
} catch (error) {
console.error('Error loading managers:', error);
alert('Failed to load managers');
}
}
// Create new department
async function createDepartment() {
const name = document.getElementById('deptName').value;
const description = document.getElementById('deptDescription').value;
const manager = document.getElementById('deptManager').value;
if (!name) {
alert('Department name is required');
return;
}
try {
const response = await fetch(`${API_BASE_URL}/departments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-auth-token': authToken
},
body: JSON.stringify({ name, description, manager })
});
if (response.ok) {
const newDept = await response.json();
alert('Department created successfully!');
closeModal('newDepartmentModal');
loadDepartments(); // Refresh department list
} else {
const error = await response.json();
alert(error.message || 'Failed to create department');
}
} catch (error) {
console.error('Error creating department:', error);
alert('Failed to create department');
}
}
// Load departments
async function loadDepartments() {
try {
const response = await fetch(`${API_BASE_URL}/departments`, {
headers: {
'x-auth-token': authToken
}
});
const departments = await response.json();
// Update the UI with departments
console.log('Loaded departments:', departments);
// TODO: Update the department cards with real data
} catch (error) {
console.error('Error loading departments:', error);
alert('Failed to load departments');
}
}
// Load recent tasks
async function loadRecentTasks() {
try {
const response = await fetch(`${API_BASE_URL}/tasks`, {
headers: {
'x-auth-token': authToken
}
});
const tasks = await response.json();
// Update the UI with tasks
console.log('Loaded tasks:', tasks);
// TODO: Update the tasks list with real data
} catch (error) {
console.error('Error loading tasks:', error);
alert('Failed to load tasks');
}
}
// Import departments
async function importDepartments() {
const fileInput = document.getElementById('file-upload');
const file = fileInput.files[0];
if (!file) {
alert('Please select a file to upload');
return;
}
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch(`${API_BASE_URL}/departments/import`, {
method: 'POST',
headers: {
'x-auth-token': authToken
},
body: formData
});
if (response.ok) {
alert('Departments imported successfully!');
closeModal('importModal');
loadDepartments(); // Refresh department list
} else {
const error = await response.json();
alert(error.message || 'Failed to import departments');
}
} catch (error) {
console.error('Error importing departments:', error);
alert('Failed to import departments');
}
}
// Vanta.js Globe Background
VANTA.GLOBE({
el: "#vanta-globe",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x4f46e5,
backgroundColor: 0xf3f4f6,
size: 1.00
});
// Initialize feather icons
feather.replace();
// Pie Chart Data
const departmentData = {
marketing: { completed: 68, remaining: 32 },
development: { completed: 82, remaining: 18 },
sales: { completed: 54, remaining: 46 },
hr: { completed: 91, remaining: 9 },
finance: { completed: 76, remaining: 24 },
operations: { completed: 63, remaining: 37 }
};
// Draw all pie charts
Object.keys(departmentData).forEach(dept => {
drawPieChart(`${dept}-chart`, departmentData[dept].completed, departmentData[dept].remaining);
});
// Initialize Vanta.js and feather icons
function drawPieChart(containerId, completedPercent, remainingPercent) {
const width = 150;
const height = 150;
const radius = Math.min(width, height) / 2;
const svg = d3.select(`#${containerId}`)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", `translate(${width/2},${height/2})`);
const color = d3.scaleOrdinal()
.domain(["completed", "remaining"])
.range(["#10B981", "#E5E7EB"]);
const pie = d3.pie()
.value(d => d.value)
.sort(null);
const data = [
{ label: "completed", value: completedPercent },
{ label: "remaining", value: remainingPercent }
];
const arc = d3.arc()
.innerRadius(0)
.outerRadius(radius);
const arcs = svg.selectAll(".arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc");
arcs.append("path")
.attr("d", arc)
.attr("fill", d => color(d.data.label))
.attr("stroke", "#fff")
.attr("stroke-width", 2);
// Add percentage text in the middle
svg.append("text")
.attr("text-anchor", "middle")
.attr("dy", ".3em")
.style("font-size", "20px")
.style("font-weight", "bold")
.text(`${completedPercent}%`);
}
</script>
</body>
</html>