Skip to content

feat: Implement admin dashboard#12

Open
mdsaad31 wants to merge 1 commit intomainfrom
feat/admin-dashboard
Open

feat: Implement admin dashboard#12
mdsaad31 wants to merge 1 commit intomainfrom
feat/admin-dashboard

Conversation

@mdsaad31
Copy link
Owner

This commit introduces a new admin dashboard with the following features:

  • User management (view, update role, delete)
  • Public chart management (view, delete)
  • File management (view, delete)
  • Analytics overview

The backend has been updated with new admin-only API endpoints, and the frontend includes a new /admin route protected by an AdminRoute component.

This commit introduces a new admin dashboard with the following features:
- User management (view, update role, delete)
- Public chart management (view, delete)
- File management (view, delete)
- Analytics overview

The backend has been updated with new admin-only API endpoints, and the frontend includes a new /admin route protected by an AdminRoute component.
@netlify
Copy link

netlify bot commented Jul 21, 2025

Deploy Preview for excel-data-analytics failed. Why did it fail? →

Name Link
🔨 Latest commit fee582f
🔍 Latest deploy log https://app.netlify.com/projects/excel-data-analytics/deploys/687e6a28b091e00008b152de

@mdsaad31 mdsaad31 requested a review from Copilot October 18, 2025 09:21
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a comprehensive admin dashboard feature that provides administrators with centralized management capabilities for users, public charts, files, and analytics. The implementation includes both frontend components for the admin interface and backend API endpoints with proper authentication and authorization.

  • Adds admin role-based access control with protected routes and middleware
  • Creates a tabbed admin dashboard interface for managing users, charts, files, and viewing analytics
  • Implements secure backend admin API endpoints with JWT authentication and role verification

Reviewed Changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/pages/AdminDashboard.jsx Main admin dashboard component with tabbed interface
src/components/auth/AuthContext.jsx Enhanced to fetch user profile with role information
src/components/auth/AdminRoute.jsx Route protection component for admin-only pages
src/components/admin/UserManagement.jsx User management interface with role updates and deletion
src/components/admin/FileManagement.jsx File management interface for viewing and deleting files
src/components/admin/ChartManagement.jsx Chart management interface for public chart operations
src/components/admin/Analytics.jsx Analytics overview displaying system metrics
src/App.jsx Adds admin route configuration
server/routes/userProfile.js Adds authentication and authorization to existing endpoints
server/routes/admin.js New admin API endpoints with role-based access control
server/models/userProfile.model.js Adds role field to user profile schema
server/middleware/auth.js JWT authentication middleware
server/index.js Registers admin router
package.json Adds express-oauth2-jwt-bearer dependency

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


useEffect(() => {
fetchUsers();
}, []);
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchUsers function should be included in the useEffect dependency array to follow React hooks best practices and prevent potential stale closure issues.

Copilot uses AI. Check for mistakes.

useEffect(() => {
fetchFiles();
}, []);
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchFiles function should be included in the useEffect dependency array to follow React hooks best practices and prevent potential stale closure issues.

Copilot uses AI. Check for mistakes.

useEffect(() => {
fetchCharts();
}, []);
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchCharts function should be included in the useEffect dependency array to follow React hooks best practices and prevent potential stale closure issues.

Copilot uses AI. Check for mistakes.
};

fetchAnalytics();
}, []);
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchAnalytics function should be included in the useEffect dependency array to follow React hooks best practices and prevent potential stale closure issues.

Suggested change
}, []);
}, [getAccessTokenSilently]);

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +105
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});

router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}
});

// File Management
router.get('/files', async (req, res) => {
try {
const files = await FileHistory.find();
res.json(files);
} catch (error) {
res.status(500).json({ message: 'Error fetching files' });
}
});

router.delete('/files/:fileId', async (req, res) => {
try {
const deletedFile = await FileHistory.findByIdAndDelete(req.params.fileId);
if (!deletedFile) {
return res.status(404).json({ message: 'File not found' });
}
res.json({ message: 'File deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting file' });
}
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation using spaces instead of the established 2-space pattern used throughout the rest of the file.

Suggested change
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});
router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}
});
// File Management
router.get('/files', async (req, res) => {
try {
const files = await FileHistory.find();
res.json(files);
} catch (error) {
res.status(500).json({ message: 'Error fetching files' });
}
});
router.delete('/files/:fileId', async (req, res) => {
try {
const deletedFile = await FileHistory.findByIdAndDelete(req.params.fileId);
if (!deletedFile) {
return res.status(404).json({ message: 'File not found' });
}
res.json({ message: 'File deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting file' });
}
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});
router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}
});
// File Management
router.get('/files', async (req, res) => {
try {
const files = await FileHistory.find();
res.json(files);
} catch (error) {
res.status(500).json({ message: 'Error fetching files' });
}
});
router.delete('/files/:fileId', async (req, res) => {
try {
const deletedFile = await FileHistory.findByIdAndDelete(req.params.fileId);
if (!deletedFile) {
return res.status(404).json({ message: 'File not found' });
}
res.json({ message: 'File deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting file' });
}

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +83
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});

router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation using spaces instead of the established 2-space pattern used throughout the rest of the file.

Suggested change
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});
router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});
router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +105
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});

router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}
});

// File Management
router.get('/files', async (req, res) => {
try {
const files = await FileHistory.find();
res.json(files);
} catch (error) {
res.status(500).json({ message: 'Error fetching files' });
}
});

router.delete('/files/:fileId', async (req, res) => {
try {
const deletedFile = await FileHistory.findByIdAndDelete(req.params.fileId);
if (!deletedFile) {
return res.status(404).json({ message: 'File not found' });
}
res.json({ message: 'File deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting file' });
}
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation using spaces instead of the established 2-space pattern used throughout the rest of the file.

Suggested change
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});
router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}
});
// File Management
router.get('/files', async (req, res) => {
try {
const files = await FileHistory.find();
res.json(files);
} catch (error) {
res.status(500).json({ message: 'Error fetching files' });
}
});
router.delete('/files/:fileId', async (req, res) => {
try {
const deletedFile = await FileHistory.findByIdAndDelete(req.params.fileId);
if (!deletedFile) {
return res.status(404).json({ message: 'File not found' });
}
res.json({ message: 'File deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting file' });
}
try {
const charts = await SavedChart.find({ isPublic: true });
res.json(charts);
} catch (error) {
res.status(500).json({ message: 'Error fetching public charts' });
}
});
router.delete('/charts/:chartId', async (req, res) => {
try {
const deletedChart = await SavedChart.findByIdAndDelete(req.params.chartId);
if (!deletedChart) {
return res.status(404).json({ message: 'Chart not found' });
}
res.json({ message: 'Chart deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting chart' });
}
});
// File Management
router.get('/files', async (req, res) => {
try {
const files = await FileHistory.find();
res.json(files);
} catch (error) {
res.status(500).json({ message: 'Error fetching files' });
}
});
router.delete('/files/:fileId', async (req, res) => {
try {
const deletedFile = await FileHistory.findByIdAndDelete(req.params.fileId);
if (!deletedFile) {
return res.status(404).json({ message: 'File not found' });
}
res.json({ message: 'File deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting file' });
}

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +106
router.delete('/files/:fileId', async (req, res) => {
try {
const deletedFile = await FileHistory.findByIdAndDelete(req.params.fileId);
if (!deletedFile) {
return res.status(404).json({ message: 'File not found' });
}
res.json({ message: 'File deleted successfully' });
} catch (error) {
res.status(500).json({ message: 'Error deleting file' });
}
});
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation using spaces instead of the established 2-space pattern used throughout the rest of the file.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants