Skip to content

Commit 92139d1

Browse files
committed
Implement Sales Role and Dashboard Enhancements
- Added a new role check for sales users, allowing access to specific endpoints and functionalities. - Updated the sales dashboard to include a comprehensive view of team performance, products, and financial metrics. - Enhanced user redirection based on role during login and navigation, improving user experience. - Introduced dynamic filtering options for the sales dashboard, enabling users to manage team data effectively. - Added tests to validate access control for sales users and ensure proper functionality of the sales dashboard.
1 parent 3248158 commit 92139d1

File tree

10 files changed

+1061
-17
lines changed

10 files changed

+1061
-17
lines changed

app/api/teams.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from app.db.database import get_db
99
from app.db.models import DBTeam, DBTeamProduct, DBUser, DBPrivateAIKey, DBRegion, DBProduct
10-
from app.core.security import check_system_admin, check_specific_team_admin, get_current_user_from_auth
10+
from app.core.security import check_system_admin, check_specific_team_admin, get_current_user_from_auth, check_sales_or_higher
1111
from app.schemas.models import (
1212
Team, TeamCreate, TeamUpdate,
1313
TeamWithUsers, TeamMergeRequest, TeamMergeResponse
@@ -231,14 +231,14 @@ async def extend_team_trial(
231231

232232
return {"message": "Team trial extended successfully"}
233233

234-
@router.get("/sales/list-teams", response_model=SalesTeamsResponse, dependencies=[Depends(check_system_admin)])
234+
@router.get("/sales/list-teams", response_model=SalesTeamsResponse, dependencies=[Depends(check_sales_or_higher)])
235235
async def list_teams_for_sales(
236236
db: Session = Depends(get_db)
237237
):
238238
"""
239239
Get consolidated team information for sales dashboard.
240240
Returns all teams with their products, regions, spend data, and trial status.
241-
Only accessible by system admin users.
241+
Accessible by system admin and sales users.
242242
"""
243243
try:
244244
# Pre-fetch all regions once to avoid repeated queries

app/core/security.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,8 @@ async def get_private_ai_access(current_user: DBUser = Depends(get_current_user_
190190
from app.core.rbac import require_private_ai_access
191191
dependency = require_private_ai_access()
192192
return dependency.check_access(current_user)
193+
194+
async def check_sales_or_higher(current_user: DBUser = Depends(get_current_user_from_auth)):
195+
"""Check if the current user is a sales user or system admin."""
196+
dependency = require_sales_or_higher()
197+
return dependency.check_access(current_user)

frontend/src/app/admin/users/page.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const USER_ROLES = [
8181
{ value: 'admin', label: 'Admin' },
8282
{ value: 'key_creator', label: 'Key Creator' },
8383
{ value: 'read_only', label: 'Read Only' },
84+
{ value: 'sales', label: 'Sales' },
8485
];
8586

8687
type SortField = 'email' | 'team_name' | 'role' | null;
@@ -406,6 +407,15 @@ export default function UsersPage() {
406407
void fetchUsers();
407408
}, [fetchUsers]);
408409

410+
// Update role when switching between system and team user types
411+
useEffect(() => {
412+
if (isSystemUser) {
413+
setNewUserRole('admin'); // Default to admin for system users
414+
} else {
415+
setNewUserRole('read_only'); // Default to read_only for team users
416+
}
417+
}, [isSystemUser]);
418+
409419
if (isLoadingUsers) {
410420
return (
411421
<div className="flex items-center justify-center min-h-[400px]">
@@ -452,6 +462,9 @@ export default function UsersPage() {
452462
if (newUserTeamId) {
453463
userData.team_id = newUserTeamId;
454464
}
465+
} else {
466+
// For system users, also set the role
467+
userData.role = newUserRole;
455468
}
456469

457470
createUserMutation.mutate(userData);
@@ -545,7 +558,7 @@ export default function UsersPage() {
545558
</Select>
546559
</div>
547560
<div className="space-y-2">
548-
<label className="text-sm font-medium">Role</label>
561+
<label className="text-sm font-medium">Team Role</label>
549562
<Select
550563
value={newUserRole}
551564
onValueChange={setNewUserRole}
@@ -554,7 +567,7 @@ export default function UsersPage() {
554567
<SelectValue placeholder="Select a role" />
555568
</SelectTrigger>
556569
<SelectContent>
557-
{USER_ROLES.map((role) => (
570+
{USER_ROLES.filter(role => role.value !== 'sales').map((role) => (
558571
<SelectItem key={role.value} value={role.value}>
559572
{role.label}
560573
</SelectItem>
@@ -564,6 +577,23 @@ export default function UsersPage() {
564577
</div>
565578
</>
566579
)}
580+
{isSystemUser && (
581+
<div className="space-y-2">
582+
<label className="text-sm font-medium">System Role</label>
583+
<Select
584+
value={newUserRole}
585+
onValueChange={setNewUserRole}
586+
>
587+
<SelectTrigger>
588+
<SelectValue placeholder="Select a system role" />
589+
</SelectTrigger>
590+
<SelectContent>
591+
<SelectItem value="admin">Admin</SelectItem>
592+
<SelectItem value="sales">Sales</SelectItem>
593+
</SelectContent>
594+
</Select>
595+
</div>
596+
)}
567597
<DialogFooter>
568598
<Button
569599
type="submit"

frontend/src/app/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ export default function Home() {
1010

1111
useEffect(() => {
1212
if (user) {
13-
router.replace('/private-ai-keys');
13+
// Redirect sales users to their dashboard
14+
if (user.role === 'sales') {
15+
router.replace('/sales');
16+
} else {
17+
router.replace('/private-ai-keys');
18+
}
1419
} else {
1520
router.replace('/auth/login');
1621
}

0 commit comments

Comments
 (0)