Refactor to manager-centric model, add team calendar for all users

Key changes:
- Removed office-centric model (deleted offices.py, office-rules)
- Renamed to team-rules, managers are part of their own team
- Team calendar visible to all (read-only for employees)
- Admins can have a manager assigned
This commit is contained in:
Stefano Manfredi
2025-12-02 13:30:04 +00:00
parent 2ad8ba3424
commit 7168fa4b72
30 changed files with 1016 additions and 910 deletions

View File

@@ -16,17 +16,9 @@ let selectedDate = null;
let currentAssignmentId = null;
document.addEventListener('DOMContentLoaded', async () => {
if (!api.requireAuth()) return;
currentUser = await api.getCurrentUser();
currentUser = await api.requireAuth();
if (!currentUser) return;
// Only managers and admins can view
if (currentUser.role === 'employee') {
window.location.href = '/presence';
return;
}
// Initialize start date based on week start preference
const weekStartDay = currentUser.week_start_day || 0;
currentStartDate = utils.getWeekStart(new Date(), weekStartDay);
@@ -38,7 +30,7 @@ document.addEventListener('DOMContentLoaded', async () => {
});
async function loadManagers() {
const response = await api.get('/api/offices/managers/list');
const response = await api.get('/api/managers');
if (response && response.ok) {
managers = await response.json();
const select = document.getElementById('managerFilter');
@@ -48,20 +40,32 @@ async function loadManagers() {
if (currentUser.role === 'manager') {
// Manager only sees themselves
filteredManagers = managers.filter(m => m.id === currentUser.id);
} else if (currentUser.role === 'employee') {
// Employee only sees their own manager
if (currentUser.manager_id) {
filteredManagers = managers.filter(m => m.id === currentUser.manager_id);
} else {
filteredManagers = [];
}
}
filteredManagers.forEach(manager => {
const option = document.createElement('option');
option.value = manager.id;
const officeNames = manager.offices.map(o => o.name).join(', ');
option.textContent = `${manager.name} (${officeNames})`;
const userCount = manager.managed_user_count || 0;
option.textContent = `${manager.name} (${userCount} users)`;
select.appendChild(option);
});
// Auto-select if only one manager (for manager role)
// Auto-select for managers and employees (they only see their team)
if (filteredManagers.length === 1) {
select.value = filteredManagers[0].id;
}
// Hide manager filter for employees (they can only see their team)
if (currentUser.role === 'employee') {
select.style.display = 'none';
}
}
}
@@ -129,7 +133,7 @@ function renderCalendar() {
// Build header row
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
let headerHtml = '<th>Name</th><th>Office</th>';
let headerHtml = '<th>Name</th><th>Manager</th>';
for (let i = 0; i < dayCount; i++) {
const date = new Date(startDate);
@@ -161,7 +165,7 @@ function renderCalendar() {
teamData.forEach(member => {
bodyHtml += `<tr>
<td class="member-name">${member.name || 'Unknown'}</td>
<td class="member-office">${member.office_name || '-'}</td>`;
<td class="member-manager">${member.manager_name || '-'}</td>`;
for (let i = 0; i < dayCount; i++) {
const date = new Date(startDate);
@@ -197,16 +201,18 @@ function renderCalendar() {
});
body.innerHTML = bodyHtml;
// Add click handlers to cells
body.querySelectorAll('.calendar-cell').forEach(cell => {
cell.style.cursor = 'pointer';
cell.addEventListener('click', () => {
const userId = cell.dataset.userId;
const date = cell.dataset.date;
const userName = cell.dataset.userName;
openDayModal(userId, date, userName);
// Add click handlers to cells (only for admins and managers)
if (currentUser.role === 'admin' || currentUser.role === 'manager') {
body.querySelectorAll('.calendar-cell').forEach(cell => {
cell.style.cursor = 'pointer';
cell.addEventListener('click', () => {
const userId = cell.dataset.userId;
const date = cell.dataset.date;
const userName = cell.dataset.userName;
openDayModal(userId, date, userName);
});
});
});
}
}
function openDayModal(userId, dateStr, userName) {