Added role members to view
This commit is contained in:
parent
3b2d4674f6
commit
a4f0fb3ea6
2 changed files with 143 additions and 1 deletions
|
|
@ -10,7 +10,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_400_BAD_REQUEST, HTTP_403_FORBIDDEN
|
||||
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
|
||||
|
||||
from apps.accounts.models import Organization, Role
|
||||
from apps.accounts.models import Organization, Role, User
|
||||
from apps.accounts.permissions import CanManageOrganization, can_manage_organization
|
||||
from apps.onboarding.models import AgentConfig, AgentInteractionLog, OnboardingFlow, OnboardingSession
|
||||
from apps.onboarding.serializers import AgentConfigSerializer, AgentInteractionLogSerializer, OnboardingFlowSerializer, OnboardingSessionSerializer
|
||||
|
|
@ -309,12 +309,115 @@ class OnboardingSessionViewSet(ModelViewSet):
|
|||
if role_uuid:
|
||||
queryset = queryset.filter(role__uuid=role_uuid)
|
||||
|
||||
user_uuid = self.request.query_params.get('user_uuid')
|
||||
if user_uuid in (None, ''):
|
||||
user_uuid = self.request.data.get('user_uuid')
|
||||
if user_uuid:
|
||||
if not user.is_manager and str(user.uuid) != str(user_uuid):
|
||||
raise PermissionDenied('You can only view your own progress sessions.')
|
||||
queryset = queryset.filter(user__uuid=user_uuid)
|
||||
|
||||
flow_uuid = self.request.query_params.get('flow_uuid')
|
||||
if flow_uuid in (None, ''):
|
||||
flow_uuid = self.request.data.get('flow_uuid')
|
||||
if flow_uuid:
|
||||
queryset = queryset.filter(state__flow_uuid=str(flow_uuid))
|
||||
|
||||
status_value = self.request.query_params.get('status')
|
||||
if status_value:
|
||||
queryset = queryset.filter(status=status_value)
|
||||
|
||||
return queryset.order_by('-created_at')
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='progress-overview')
|
||||
def progress_overview(self, request):
|
||||
user = request.user
|
||||
|
||||
role_uuid = request.query_params.get('role_uuid')
|
||||
|
||||
if user.is_manager:
|
||||
roles_qs = Role.objects.filter(
|
||||
Q(organization__owner=user) | Q(organization__members=user)
|
||||
).distinct()
|
||||
else:
|
||||
roles_qs = Role.objects.filter(members=user)
|
||||
|
||||
if role_uuid:
|
||||
roles_qs = roles_qs.filter(uuid=role_uuid)
|
||||
|
||||
rows = []
|
||||
|
||||
for role in roles_qs.order_by('name'):
|
||||
flows = list(OnboardingFlow.objects.filter(role=role).order_by('-updated_at'))
|
||||
if not flows:
|
||||
continue
|
||||
|
||||
if user.is_manager:
|
||||
learners = list(role.members.all().order_by('first_name', 'last_name', 'email_address'))
|
||||
else:
|
||||
learners = [user] if role.members.filter(id=user.id).exists() else []
|
||||
|
||||
if not learners:
|
||||
continue
|
||||
|
||||
role_sessions = list(
|
||||
OnboardingSession.objects.filter(role=role, user__in=learners)
|
||||
.select_related('user')
|
||||
.order_by('-updated_at')
|
||||
)
|
||||
|
||||
latest_by_user_flow = {}
|
||||
for session in role_sessions:
|
||||
state = session.state if isinstance(session.state, dict) else {}
|
||||
session_flow_uuid = str(state.get('flow_uuid') or '')
|
||||
if not session_flow_uuid:
|
||||
continue
|
||||
|
||||
key = (session.user_id, session_flow_uuid)
|
||||
if key not in latest_by_user_flow:
|
||||
latest_by_user_flow[key] = session
|
||||
|
||||
for flow in flows:
|
||||
flow_uuid_str = str(flow.uuid)
|
||||
for learner in learners:
|
||||
latest_session = latest_by_user_flow.get((learner.id, flow_uuid_str))
|
||||
|
||||
latest_status = latest_session.status if latest_session else 'not_started'
|
||||
state = latest_session.state if latest_session and isinstance(latest_session.state, dict) else {}
|
||||
progress = state.get('progress_percentage', state.get('progress', 0))
|
||||
|
||||
rows.append({
|
||||
'role': {
|
||||
'uuid': str(role.uuid),
|
||||
'name': role.name,
|
||||
},
|
||||
'user': {
|
||||
'uuid': str(learner.uuid),
|
||||
'name': str(getattr(learner, 'full_name', '')).strip() or learner.email_address,
|
||||
'email': learner.email_address,
|
||||
},
|
||||
'flow': {
|
||||
'uuid': flow_uuid_str,
|
||||
'title': flow.title,
|
||||
'is_active': flow.is_active,
|
||||
},
|
||||
'latest_status': latest_status,
|
||||
'progress': int(progress) if isinstance(progress, (int, float)) else 0,
|
||||
'is_completed': latest_status == 'completed',
|
||||
'latest_session_uuid': str(latest_session.uuid) if latest_session else None,
|
||||
'updated_at': latest_session.updated_at.isoformat() if latest_session and latest_session.updated_at else None,
|
||||
})
|
||||
|
||||
rows.sort(
|
||||
key=lambda row: (
|
||||
str(row.get('role', {}).get('name', '')),
|
||||
str(row.get('user', {}).get('name', '')),
|
||||
str(row.get('flow', {}).get('title', '')),
|
||||
)
|
||||
)
|
||||
|
||||
return Response(rows, status=HTTP_200_OK)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
return Response(
|
||||
{'error': 'Use onboarding-flow/<uuid>/start-session/ to create a session.'},
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ const loading = ref(false)
|
|||
const creatingRole = ref(false)
|
||||
const deletingRoleUuid = ref<string | null>(null)
|
||||
const roleModalVisible = ref(false)
|
||||
const roleMembersModalVisible = ref(false)
|
||||
const selectedRoleForMembers = ref<Role | null>(null)
|
||||
const selectedRoleMembers = ref<User[]>([])
|
||||
const createRoleForm = ref({
|
||||
name: '',
|
||||
description: '',
|
||||
|
|
@ -186,6 +189,12 @@ const deleteRole = async (role: Role) => {
|
|||
})
|
||||
}
|
||||
|
||||
const openRoleMembersModal = (role: Role) => {
|
||||
selectedRoleForMembers.value = role
|
||||
selectedRoleMembers.value = Array.isArray(role.members) ? role.members : []
|
||||
roleMembersModalVisible.value = true
|
||||
}
|
||||
|
||||
const createInvite = async () => {
|
||||
try {
|
||||
const response = await apiClient.post<InviteToken>(
|
||||
|
|
@ -482,6 +491,9 @@ onMounted(async () => {
|
|||
/>
|
||||
<Space>
|
||||
<Tag>{{ item.member_count }} members</Tag>
|
||||
<Button size="small" @click="openRoleMembersModal(item)">
|
||||
View Members
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
size="small"
|
||||
|
|
@ -548,6 +560,33 @@ onMounted(async () => {
|
|||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
v-model:open="roleMembersModalVisible"
|
||||
:title="`Members in ${selectedRoleForMembers?.name || 'Role'}`"
|
||||
:footer="null"
|
||||
>
|
||||
<List
|
||||
v-if="selectedRoleMembers.length > 0"
|
||||
:data-source="selectedRoleMembers"
|
||||
:bordered="false"
|
||||
>
|
||||
<template #renderItem="{ item }">
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
:title="`${item.first_name} ${item.last_name}`"
|
||||
:description="item.email_address"
|
||||
/>
|
||||
<Tag :color="item.is_manager ? 'purple' : 'default'">
|
||||
{{ item.is_manager ? 'Manager' : 'Member' }}
|
||||
</Tag>
|
||||
</List.Item>
|
||||
</template>
|
||||
</List>
|
||||
<Typography.Paragraph v-else type="secondary">
|
||||
No members assigned to this role yet.
|
||||
</Typography.Paragraph>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue