Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added invite list to admin dashboard #145

Merged
merged 1 commit into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions CoVAR-app/src/app/(pages)/dashboard/components/InviteList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use client';
import React, { useEffect, useState } from 'react';
import { Paper, Typography, Box, CircularProgress, List, ListItem, ListItemText } from '@mui/material';
import { fetchAllInvites } from '@/functions/requests';

type Invite = {
invite_id: string;
username: string;
organization_name: string;
invite_status: string;
};

const InviteList: React.FC = () => {
const [invites, setInvites] = useState<Invite[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
const loadInvites = async () => {
try {
const token = localStorage.getItem('accessToken');
const invites = await fetchAllInvites(token as string);
setInvites(invites);
setLoading(false);
} catch (error) {
console.error('Error fetching invites:', error);
setLoading(false);
}
};

loadInvites();
}, []);

return (
<Box sx={{ padding: 2 }}>
<Typography variant="h6" color="text.primary">Pending Invites</Typography>
{loading ? (
<CircularProgress />
) : invites.length === 0 ? (
<Typography>No pending invites at the moment.</Typography>
) : (
<List>
{invites.map((invite) => (
<ListItem key={invite.invite_id}>
<ListItemText
primary={`${invite.username} - ${invite.organization_name}`}
secondary={`Status: ${invite.invite_status}`}
/>
</ListItem>
))}
</List>
)}
</Box>
);
};

export default InviteList;
68 changes: 34 additions & 34 deletions CoVAR-app/src/app/(pages)/dashboard/components/adminPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { Height } from '@mui/icons-material';
import { mainContentStyles } from '@/styles/sidebarStyle';
import SeverityDistribution from './severityDistribution';
import { chartContainerStyles } from '@/styles/dashboardStyle';
import InviteList from './InviteList'; // Import the new component


type User = {
user_id: string;
Expand Down Expand Up @@ -160,49 +162,47 @@ const AdminPage: React.FC = () => {
{ name: 'Clients', value: userMetrics.clients },
];

return (

<Box sx={mainContentStyles}>
<Box sx = {{marginBottom : '2vh'}}>
<Typography variant="h5" color="text.primary">
User Metrics
</Typography>
</Box>


<Paper sx={{ padding: 2 }}>
<Typography>Total Users: {userMetrics.total}</Typography>
<Typography>Admins: {userMetrics.admins}</Typography>
<Typography>VAs: {userMetrics.vas}</Typography>
<Typography>Clients: {userMetrics.clients}</Typography>
<Paper sx={chartContainerStyles}>
return (
<Box sx={mainContentStyles}>
<Box sx={{ marginBottom: '2vh' }}>
<Typography variant="h5" color="text.primary">
User Metrics
</Typography>
</Box>

<Paper sx={{ padding: 2 }}>
<Typography>Total Users: {userMetrics.total}</Typography>
<Typography>Admins: {userMetrics.admins}</Typography>
<Typography>VAs: {userMetrics.vas}</Typography>
<Typography>Clients: {userMetrics.clients}</Typography>

<Box sx={{ display: 'flex', justifyContent: 'space-between', marginTop: 2 }}>
<Paper sx={{ ...chartContainerStyles, width: '48%' }}>
<Typography variant="h6">Role Distribution</Typography>
<SeverityDistribution data={roleData} />
</Paper>
</Paper>



<Box sx = {{marginBottom : '2vh', marginTop : '2vh'}}>
<Typography variant="h5" color="text.primary">
Unauthorised Users (Last Week)
</Typography>
</Box>

<Paper sx={{ ...chartContainerStyles, width: '48%' }}>
<InviteList />
</Paper>
</Box>
</Paper>

<Box sx={{ marginBottom: '2vh', marginTop: '2vh' }}>
<Typography variant="h5" color="text.primary">
Unauthorised Users (Last Week)
</Typography>
</Box>

{loading ? (
<CircularProgress />
) : (

<DataGrid
rows={users}
columns={columns}
getRowId={(row) => row.user_id}
sx = {{...dataGridStyles,
height : '50vh'
}}
/>
<DataGrid
rows={users}
columns={columns}
getRowId={(row) => row.user_id}
sx={{ ...dataGridStyles, height: '50vh' }}
/>
)}

<ConfirmAuthorizeDialog
Expand Down
11 changes: 11 additions & 0 deletions CoVAR-app/src/functions/requests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -733,3 +733,14 @@ export const getOwner = async (orgId: string, accessToken: string) => {
};
return await handleRequest(request);
};


// Function to fetch all invites
export const fetchAllInvites = async (accessToken: string) => {
const request = {
method: 'get',
url: `/api/invites`,
headers: { Authorization: `Bearer ${accessToken}` },
};
return await handleRequest(request);
};
3 changes: 2 additions & 1 deletion langchain/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ requests
python-dotenv
openai
PyJWT
cryptography
cryptography
gunicorn
29 changes: 22 additions & 7 deletions server/routes/invites.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
const express = require('express');

const { authenticateToken} = require('../lib/securityFunctions');

const { authenticateToken } = require('../lib/securityFunctions');
const pgClient = require('../lib/postgres');

const router = express.Router();

// Fetch invites for a user
// Fetch invites for a specific user
router.get('/invites/:username', authenticateToken, async (req, res) => {
const { username } = req.params;

Expand All @@ -19,7 +16,7 @@ router.get('/invites/:username', authenticateToken, async (req, res) => {

const userId = userResult.rows[0].user_id;

// Fetch invites for the user
// Fetch pending invites for the user
const invitesQuery = `
SELECT i.invite_id, o.name as organization_name, i.invite_status
FROM organization_invites i
Expand Down Expand Up @@ -82,5 +79,23 @@ router.patch('/invites/:inviteId/reject', authenticateToken, async (req, res) =>
}
});

// Fetch all invites
router.get('/invites', authenticateToken, async (req, res) => {
try {
const invitesQuery = `
SELECT i.invite_id, u.username, o.name as organization_name, i.invite_status
FROM organization_invites i
JOIN organizations o ON i.organization_id = o.organization_id
JOIN users u ON i.user_id = u.user_id
`;
const invitesResult = await pgClient.query(invitesQuery);

res.json(invitesResult.rows);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});


module.exports = router;
module.exports = router;