Files
panel/AUTHORIZATION_IMPLEMENTATION.md
Chop 90875db28b feat: Add user tracking to project tasks and notes
- Implemented user tracking columns in project_tasks and notes tables.
- Added created_by and assigned_to fields to project_tasks.
- Introduced created_by field and is_system flag in notes.
- Updated API endpoints to handle user tracking during task and note creation.
- Enhanced database initialization to include new columns and indexes.
- Created utility functions to fetch users for task assignment.
- Updated front-end components to display user information for tasks and notes.
- Added tests for project-tasks API endpoints to verify functionality.
2025-06-26 00:17:51 +02:00

27 KiB

Authorization Implementation Guide

Project Overview

This document outlines the implementation strategy for adding authentication and authorization to the Project Management Panel - a Next.js 15 application with SQLite database.

Current State Analysis (Updated: June 25, 2025)

What We Have Implemented

  • Framework: Next.js 15 with App Router
  • Database: SQLite with better-sqlite3
  • Authentication: NextAuth.js v5 with credentials provider
  • User Management: Complete user CRUD operations with bcrypt password hashing
  • Database Schema: Users table with roles, audit logs, sessions
  • API Protection: Middleware system with role-based access control
  • Session Management: JWT-based sessions with 30-day expiration
  • Security Features: Account lockout, failed login tracking, password validation
  • UI Components: Authentication provider, navigation with user context
  • Auth Pages: Sign-in page implemented

What's Protected

  • API Routes: All major endpoints (projects, contracts, tasks, notes) are protected
  • Role Hierarchy: admin > project_manager > user > read_only
  • Navigation: Role-based menu items (admin sees user management)
  • Session Security: Automatic session management and validation

🔄 Partially Implemented

  • Auth Pages: Sign-in exists, missing sign-out and error pages
  • User Interface: Basic auth integration, could use more polish
  • Admin Features: User management backend exists, UI needs completion
  • Audit Logging: Database schema exists, not fully integrated

Still Missing

  • Complete user management UI for admins
  • Password reset functionality
  • Rate limiting implementation
  • Enhanced input validation schemas
  • CSRF protection
  • Security headers middleware
  • Comprehensive error handling
  • Email notifications

1. Authentication Solution: NextAuth.js

Why NextAuth.js?

  • Native Next.js 15 App Router support
  • Database session management
  • Built-in security features (CSRF, JWT handling)
  • Flexible provider system
  • SQLite adapter available

2. Role-Based Access Control (RBAC)

Proposed User Roles:

Role Permissions Use Case
Admin Full system access, user management System administrators
Project Manager Manage all projects/tasks, view reports Team leads, supervisors
User View/edit assigned projects/tasks Regular employees
Read-only View-only access to data Clients, stakeholders

Implementation Status

Phase 1: Foundation Setup - COMPLETED

1.1 Dependencies - INSTALLED

  • NextAuth.js v5 (beta)
  • bcryptjs for password hashing
  • Zod for validation
  • Better-sqlite3 adapter compatibility

1.2 Environment Configuration - COMPLETED

  • .env.local configured with NEXTAUTH_SECRET and NEXTAUTH_URL
  • Database URL configuration
  • Development environment setup

1.3 Database Schema - IMPLEMENTED

  • Users table with roles and security features
  • Sessions table for NextAuth.js
  • Audit logs table for security tracking
  • Proper indexes for performance

1.4 Initial Admin User - COMPLETED

  • scripts/create-admin.js script available
  • Default admin user: admin@localhost.com / admin123456

Phase 2: Authentication Core - COMPLETED

2.1 NextAuth.js Configuration - IMPLEMENTED

  • File: src/lib/auth.js
  • Credentials provider with email/password
  • JWT session strategy with 30-day expiration
  • Account lockout after 5 failed attempts (15-minute lockout)
  • Password verification with bcrypt
  • Failed login attempt tracking
  • Session callbacks for role management

2.2 API Route Handlers - IMPLEMENTED

  • File: src/app/api/auth/[...nextauth]/route.js
  • NextAuth.js handlers properly configured

2.3 User Management System - IMPLEMENTED

  • File: src/lib/userManagement.js
  • Complete CRUD operations for users
  • Password hashing and validation
  • Role management functions
  • User lookup by ID and email

Phase 3: Authorization Middleware - COMPLETED

3.1 API Protection Middleware - IMPLEMENTED

  • File: src/lib/middleware/auth.js
  • withAuth() function for protecting routes
  • Role hierarchy enforcement (admin=4, project_manager=3, user=2, read_only=1)
  • Helper functions: withReadAuth, withUserAuth, withAdminAuth, withManagerAuth
  • Proper error handling and status codes

3.2 Protected API Routes - IMPLEMENTED

Example in src/app/api/projects/route.js:

  • GET requests require read_only access
  • POST requests require user access
  • All major API endpoints are protected

3.3 Session Provider - IMPLEMENTED

  • File: src/components/auth/AuthProvider.js
  • NextAuth SessionProvider wrapper
  • Integrated into root layout

🔄 Phase 4: User Interface - PARTIALLY COMPLETED

4.1 Authentication Pages - 🔄 PARTIAL

  • Sign-in page: src/app/auth/signin/page.js - Complete with form validation
  • Sign-out page: Missing
  • 🔄 Error page: src/app/auth/error/page.js - Basic implementation
  • Unauthorized page: Missing

4.2 Navigation Updates - COMPLETED

  • File: src/components/ui/Navigation.js
  • User session integration with useSession
  • Role-based menu items (admin sees user management)
  • Sign-out functionality
  • Conditional rendering based on auth status

4.3 User Management Interface - MISSING

  • Backend exists in userManagement.js
  • Admin UI for user CRUD operations needed
  • Role assignment interface needed

Phase 5: Security Enhancements - NOT STARTED

5.1 Input Validation Schemas - MISSING

  • Zod schemas for API endpoints
  • Request validation middleware

5.2 Rate Limiting - MISSING

  • Rate limiting middleware
  • IP-based request tracking

5.3 Security Headers - MISSING

  • CSRF protection
  • Security headers middleware
  • Content Security Policy
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { BetterSQLite3Adapter } from "@auth/better-sqlite3-adapter";
import db from "./db.js";
import bcrypt from "bcryptjs";
import { z } from "zod";

const loginSchema = z.object({
	email: z.string().email("Invalid email format"),
	password: z.string().min(6, "Password must be at least 6 characters"),
});

export const { handlers, auth, signIn, signOut } = NextAuth({
	adapter: BetterSQLite3Adapter(db),
	session: {
		strategy: "database",
		maxAge: 30 * 24 * 60 * 60, // 30 days
		updateAge: 24 * 60 * 60, // 24 hours
	},
	providers: [
		CredentialsProvider({
			name: "credentials",
			credentials: {
				email: { label: "Email", type: "email" },
				password: { label: "Password", type: "password" },
			},
			async authorize(credentials, req) {
				try {
					// Validate input
					const validatedFields = loginSchema.parse(credentials);

					// Check if user exists and is active
					const user = db
						.prepare(
							`
            SELECT id, email, name, password_hash, role, is_active, 
                   failed_login_attempts, locked_until
            FROM users 
            WHERE email = ? AND is_active = 1
          `
						)
						.get(validatedFields.email);

					if (!user) {
						throw new Error("Invalid credentials");
					}

					// Check if account is locked
					if (user.locked_until && new Date(user.locked_until) > new Date()) {
						throw new Error("Account temporarily locked");
					}

					// Verify password
					const isValidPassword = await bcrypt.compare(
						validatedFields.password,
						user.password_hash
					);

					if (!isValidPassword) {
						// Increment failed attempts
						db.prepare(
							`
              UPDATE users 
              SET failed_login_attempts = failed_login_attempts + 1,
                  locked_until = CASE 
                    WHEN failed_login_attempts >= 4 
                    THEN datetime('now', '+15 minutes')
                    ELSE locked_until 
                  END
              WHERE id = ?
            `
						).run(user.id);

						throw new Error("Invalid credentials");
					}

					// Reset failed attempts and update last login
					db.prepare(
						`
            UPDATE users 
            SET failed_login_attempts = 0, 
                locked_until = NULL,
                last_login = CURRENT_TIMESTAMP
            WHERE id = ?
          `
					).run(user.id);

					// Log successful login
					logAuditEvent(user.id, "LOGIN_SUCCESS", "user", user.id, req);

					return {
						id: user.id,
						email: user.email,
						name: user.name,
						role: user.role,
					};
				} catch (error) {
					console.error("Login error:", error);
					return null;
				}
			},
		}),
	],
	callbacks: {
		async jwt({ token, user, account }) {
			if (user) {
				token.role = user.role;
				token.userId = user.id;
			}
			return token;
		},
		async session({ session, token, user }) {
			if (token) {
				session.user.id = token.userId || token.sub;
				session.user.role = token.role || user?.role;
			}
			return session;
		},
		async signIn({ user, account, profile, email, credentials }) {
			// Additional sign-in logic if needed
			return true;
		},
	},
	pages: {
		signIn: "/auth/signin",
		signOut: "/auth/signout",
		error: "/auth/error",
	},
	events: {
		async signOut({ session, token }) {
			if (session?.user?.id) {
				logAuditEvent(session.user.id, "LOGOUT", "user", session.user.id);
			}
		},
	},
});

// Audit logging helper
function logAuditEvent(userId, action, resourceType, resourceId, req = null) {
	try {
		db.prepare(
			`
      INSERT INTO audit_logs (user_id, action, resource_type, resource_id, ip_address, user_agent)
      VALUES (?, ?, ?, ?, ?, ?)
    `
		).run(
			userId,
			action,
			resourceType,
			resourceId,
			req?.ip || "unknown",
			req?.headers?.["user-agent"] || "unknown"
		);
	} catch (error) {
		console.error("Audit log error:", error);
	}
}

2.2 API Route Handlers

Create src/app/api/auth/[...nextauth]/route.js:

import { handlers } from "@/lib/auth";

export const { GET, POST } = handlers;

Phase 3: Authorization Middleware

3.1 API Protection Middleware

Create src/lib/middleware/auth.js:

import { auth } from "@/lib/auth";
import { NextResponse } from "next/server";
import { z } from "zod";

// Role hierarchy for permission checking
const ROLE_HIERARCHY = {
	admin: 4,
	project_manager: 3,
	user: 2,
	read_only: 1,
};

export function withAuth(handler, options = {}) {
	return async (req, context) => {
		try {
			const session = await auth();

			// Check if user is authenticated
			if (!session?.user) {
				return NextResponse.json(
					{ error: "Authentication required" },
					{ status: 401 }
				);
			}

			// Check if user account is active
			const user = db
				.prepare("SELECT is_active FROM users WHERE id = ?")
				.get(session.user.id);
			if (!user?.is_active) {
				return NextResponse.json(
					{ error: "Account deactivated" },
					{ status: 403 }
				);
			}

			// Check role-based permissions
			if (
				options.requiredRole &&
				!hasPermission(session.user.role, options.requiredRole)
			) {
				logAuditEvent(
					session.user.id,
					"ACCESS_DENIED",
					options.resource || "api",
					req.url
				);
				return NextResponse.json(
					{ error: "Insufficient permissions" },
					{ status: 403 }
				);
			}

			// Check resource-specific permissions
			if (options.checkResourceAccess) {
				const hasAccess = await options.checkResourceAccess(
					session.user,
					context.params
				);
				if (!hasAccess) {
					return NextResponse.json(
						{ error: "Access denied to this resource" },
						{ status: 403 }
					);
				}
			}

			// Validate request body if schema provided
			if (
				options.bodySchema &&
				(req.method === "POST" ||
					req.method === "PUT" ||
					req.method === "PATCH")
			) {
				try {
					const body = await req.json();
					options.bodySchema.parse(body);
				} catch (error) {
					return NextResponse.json(
						{ error: "Invalid request data", details: error.errors },
						{ status: 400 }
					);
				}
			}

			// Add user info to request
			req.user = session.user;
			req.session = session;

			// Call the original handler
			return await handler(req, context);
		} catch (error) {
			console.error("Auth middleware error:", error);
			return NextResponse.json(
				{ error: "Internal server error" },
				{ status: 500 }
			);
		}
	};
}

export function hasPermission(userRole, requiredRole) {
	return ROLE_HIERARCHY[userRole] >= ROLE_HIERARCHY[requiredRole];
}

// Helper for read-only operations
export function withReadAuth(handler) {
	return withAuth(handler, { requiredRole: "read_only" });
}

// Helper for user-level operations
export function withUserAuth(handler) {
	return withAuth(handler, { requiredRole: "user" });
}

// Helper for project manager operations
export function withManagerAuth(handler) {
	return withAuth(handler, { requiredRole: "project_manager" });
}

// Helper for admin operations
export function withAdminAuth(handler) {
	return withAuth(handler, { requiredRole: "admin" });
}

3.2 Client-Side Route Protection

Create src/components/auth/ProtectedRoute.js:

"use client";

import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useEffect } from "react";

export function ProtectedRoute({
	children,
	requiredRole = null,
	fallback = null,
}) {
	const { data: session, status } = useSession();
	const router = useRouter();

	useEffect(() => {
		if (status === "loading") return; // Still loading

		if (!session) {
			router.push("/auth/signin");
			return;
		}

		if (requiredRole && !hasPermission(session.user.role, requiredRole)) {
			router.push("/unauthorized");
			return;
		}
	}, [session, status, router, requiredRole]);

	if (status === "loading") {
		return (
			<div className="flex justify-center items-center h-64">Loading...</div>
		);
	}

	if (!session) {
		return fallback || <div>Redirecting to login...</div>;
	}

	if (requiredRole && !hasPermission(session.user.role, requiredRole)) {
		return fallback || <div>Access denied</div>;
	}

	return children;
}

function hasPermission(userRole, requiredRole) {
	const roleHierarchy = {
		admin: 4,
		project_manager: 3,
		user: 2,
		read_only: 1,
	};

	return roleHierarchy[userRole] >= roleHierarchy[requiredRole];
}

Phase 4: User Interface Components

4.1 Authentication Pages

Pages to create:

  • src/app/auth/signin/page.js - Login form
  • src/app/auth/signout/page.js - Logout confirmation
  • src/app/auth/error/page.js - Error handling
  • src/app/unauthorized/page.js - Access denied page

4.2 Navigation Updates

Update src/components/ui/Navigation.js to include:

  • Login/logout buttons
  • User info display
  • Role-based menu items

4.3 User Management Interface

For admin users:

  • User listing and management
  • Role assignment
  • Account activation/deactivation

Phase 5: Security Enhancements

5.1 Input Validation Schemas

Create src/lib/schemas/ with Zod schemas for all API endpoints:

// src/lib/schemas/project.js
import { z } from "zod";

export const createProjectSchema = z.object({
	contract_id: z.number().int().positive(),
	project_name: z.string().min(1).max(255),
	project_number: z.string().min(1).max(50),
	address: z.string().optional(),
	// ... other fields
});

export const updateProjectSchema = createProjectSchema.partial();

5.2 Rate Limiting

Implement rate limiting for sensitive endpoints:

// src/lib/middleware/rateLimit.js
const attempts = new Map();

export function withRateLimit(
	handler,
	options = { maxAttempts: 5, windowMs: 15 * 60 * 1000 }
) {
	return async (req, context) => {
		const key = req.ip || "unknown";
		const now = Date.now();
		const window = attempts.get(key) || {
			count: 0,
			resetTime: now + options.windowMs,
		};

		if (now > window.resetTime) {
			window.count = 1;
			window.resetTime = now + options.windowMs;
		} else {
			window.count++;
		}

		attempts.set(key, window);

		if (window.count > options.maxAttempts) {
			return NextResponse.json({ error: "Too many requests" }, { status: 429 });
		}

		return handler(req, context);
	};
}

Implementation Checklist (Updated Status)

Phase 1: Foundation - COMPLETED

  • Install dependencies (NextAuth.js v5, bcryptjs, zod)
  • Create environment configuration (.env.local)
  • Extend database schema (users, sessions, audit_logs)
  • Create initial admin user script

Phase 2: Authentication - COMPLETED

  • Configure NextAuth.js with credentials provider
  • Create API route handlers (/api/auth/[...nextauth])
  • Implement user management system
  • Test login/logout functionality

Phase 3: Authorization - COMPLETED

  • Implement API middleware (withAuth, role hierarchy)
  • Protect existing API routes (projects, contracts, tasks, notes)
  • Create role-based helper functions
  • Integrate session provider in app layout

🔄 Phase 4: User Interface - IN PROGRESS

  • Create sign-in page with form validation
  • Update navigation component with auth integration
  • Add role-based menu items
  • Create sign-out confirmation page
  • Create error handling page
  • Create unauthorized access page
  • Build admin user management interface

Phase 5: Security Enhancements - NOT STARTED

  • Add input validation schemas to all endpoints
  • Implement rate limiting for sensitive operations
  • Add comprehensive audit logging
  • Create security headers middleware
  • Implement CSRF protection
  • Add password reset functionality

Current Working Features

🔐 Authentication System

  • Login/Logout: Fully functional with NextAuth.js
  • Session Management: JWT-based with 30-day expiration
  • Password Security: bcrypt hashing with salt rounds
  • Account Lockout: 5 failed attempts = 15-minute lockout
  • Role System: 4-tier hierarchy (admin, project_manager, user, read_only)

🛡️ Authorization System

  • API Protection: All major endpoints require authentication
  • Role-Based Access: Different permission levels per endpoint
  • Middleware: Clean abstraction with helper functions
  • Session Validation: Automatic session verification

📱 User Interface

  • Navigation: Context-aware with user info and sign-out
  • Auth Pages: Professional sign-in form with error handling
  • Role Integration: Admin users see additional menu items
  • Responsive: Works across device sizes

🗄️ Database Security

  • User Management: Complete CRUD with proper validation
  • Audit Schema: Ready for comprehensive logging
  • Indexes: Optimized for performance
  • Constraints: Role validation and data integrity

Next Priority Tasks

  1. Complete Auth UI (High Priority)

    • Sign-out confirmation page
    • Unauthorized access page
    • Enhanced error handling
  2. Admin User Management (High Priority)

    • User listing interface
    • Create/edit user forms
    • Role assignment controls
  3. Security Enhancements (Medium Priority)

    • Input validation schemas
    • Rate limiting middleware
    • Comprehensive audit logging
  4. Password Management (Medium Priority)

    • Password reset functionality
    • Password strength requirements
    • Password change interface

User Tracking in Projects - NEW FEATURE

📊 Project User Management Implementation

We've successfully implemented comprehensive user tracking for projects:

Database Schema Updates

  • created_by: Tracks who created the project (user ID)
  • assigned_to: Tracks who is assigned to work on the project (user ID)
  • created_at: Timestamp when project was created
  • updated_at: Timestamp when project was last modified
  • Indexes: Performance optimized with proper foreign key indexes

API Enhancements

  • Enhanced Queries: Projects now include user names and emails via JOIN operations
  • User Assignment: New /api/projects/users endpoint for user management
  • Query Filters: Support for filtering projects by assigned user or creator
  • User Context: Create/update operations automatically capture authenticated user ID

UI Components

  • Project Form: User assignment dropdown in create/edit forms
  • Project Listing: "Created By" and "Assigned To" columns in project table
  • User Selection: Dropdown populated with active users for assignment

New Query Functions

  • getAllUsersForAssignment(): Get active users for assignment dropdown
  • getProjectsByAssignedUser(userId): Filter projects by assignee
  • getProjectsByCreator(userId): Filter projects by creator
  • updateProjectAssignment(projectId, userId): Update project assignment

Security Integration

  • Authentication Required: All user operations require valid session
  • Role-Based Access: User assignment respects role hierarchy
  • Audit Ready: Infrastructure prepared for comprehensive user action logging

Usage Examples

Creating Projects with User Tracking

// Projects are automatically assigned to the authenticated user as creator
POST /api/projects
{
  "project_name": "New Project",
  "assigned_to": "user-id-here", // Optional assignment
  // ... other project data
}

Filtering Projects by User

// Get projects assigned to specific user
GET /api/projects?assigned_to=user-id

// Get projects created by specific user
GET /api/projects?created_by=user-id

Updating Project Assignment

POST /api/projects/users
{
  "projectId": 123,
  "assignedToUserId": "new-user-id"
}

Project Tasks User Tracking - NEW FEATURE

📋 Task User Management Implementation

We've also implemented comprehensive user tracking for project tasks:

Database Schema Updates

  • created_by: Tracks who created the task (user ID)
  • assigned_to: Tracks who is assigned to work on the task (user ID)
  • created_at: Timestamp when task was created
  • updated_at: Timestamp when task was last modified
  • Indexes: Performance optimized with proper foreign key indexes

API Enhancements

  • Enhanced Queries: Tasks now include user names and emails via JOIN operations
  • User Assignment: New /api/project-tasks/users endpoint for user management
  • Query Filters: Support for filtering tasks by assigned user or creator
  • User Context: Create/update operations automatically capture authenticated user ID

UI Components

  • Task Form: User assignment dropdown in create task forms
  • Task Listing: "Created By" and "Assigned To" columns in task table
  • User Selection: Dropdown populated with active users for assignment

New Task Query Functions

  • getAllUsersForTaskAssignment(): Get active users for assignment dropdown
  • getProjectTasksByAssignedUser(userId): Filter tasks by assignee
  • getProjectTasksByCreator(userId): Filter tasks by creator
  • updateProjectTaskAssignment(taskId, userId): Update task assignment

Task Creation Behavior

  • Auto-assignment: Tasks are automatically assigned to the authenticated user as creator
  • Optional Assignment: Users can assign tasks to other team members during creation
  • Creator Tracking: All tasks track who created them for accountability

Task Usage Examples

Creating Tasks with User Tracking

// Tasks are automatically assigned to the authenticated user as creator
POST /api/project-tasks
{
  "project_id": 123,
  "task_template_id": 1, // or custom_task_name for custom tasks
  "assigned_to": "user-id-here", // Optional, defaults to creator
  "priority": "high"
}

Filtering Tasks by User

// Get tasks assigned to specific user
GET /api/project-tasks?assigned_to=user-id

// Get tasks created by specific user
GET /api/project-tasks?created_by=user-id

Updating Task Assignment

POST /api/project-tasks/users
{
  "taskId": 456,
  "assignedToUserId": "new-user-id"
}

Next Enhancements

  1. Dashboard Views (Recommended)

    • "My Projects" dashboard showing assigned projects
    • Project creation history per user
    • Workload distribution reports
  2. Advanced Filtering (Future)

    • Multi-user assignment support
    • Team-based project assignments
    • Role-based project visibility
  3. Notifications (Future)

    • Email alerts on project assignment
    • Deadline reminders for assigned users
    • Status change notifications

Notes User Tracking - NEW FEATURE

📝 Notes User Management Implementation

We've also implemented comprehensive user tracking for all notes (both project notes and task notes):

Database Schema Updates

  • created_by: Tracks who created the note (user ID)
  • is_system: Distinguishes between user notes and system-generated notes
  • Enhanced queries: Notes now include user names and emails via JOIN operations
  • Indexes: Performance optimized with proper indexes for user lookups

API Enhancements

  • User Context: All note creation operations automatically capture authenticated user ID
  • System Notes: Automatic system notes (task status changes) track who made the change
  • User Information: Note retrieval includes creator name and email for display

UI Components

  • Project Notes: Display creator name and email in project note listings
  • Task Notes: Show who added each note with user badges and timestamps
  • System Notes: Distinguished from user notes with special styling and "System" badge
  • User Attribution: Clear indication of who created each note and when

New Note Query Functions

  • getAllNotesWithUsers(): Get all notes with user and project/task context
  • getNotesByCreator(userId): Filter notes by creator for user activity tracking
  • Enhanced getNotesByProjectId() and getNotesByTaskId() with user information

Automatic User Tracking

  • Note Creation: All new notes automatically record who created them
  • System Notes: Task status changes generate system notes attributed to the user who made the change
  • Audit Trail: Complete history of who added what notes and when

Notes Usage Examples

Project Notes with User Tracking

  • Notes display creator name in a blue badge next to the timestamp
  • Form automatically associates notes with the authenticated user
  • Clear visual distinction between different note authors

Task Notes with User Tracking

  • User notes show creator name in a gray badge
  • System notes show "System" badge but also track the user who triggered the action
  • Full audit trail of task status changes and who made them

System Note Generation

// When a user changes a task status, a system note is automatically created:
// "Status changed from 'pending' to 'in_progress'" - attributed to the user who made the change

Benefits

  1. Accountability: Full audit trail of who added what notes
  2. Context: Know who to contact for clarification on specific notes
  3. History: Track communication and decisions made by team members
  4. System Integration: Automatic notes for system actions still maintain user attribution
  5. User Experience: Clear visual indicators of note authors improve team collaboration