Auto-assign linked executive user and persist userId in organisation update modal
This commit is contained in:
@@ -62,6 +62,7 @@ import {
|
|||||||
assignUserRole,
|
assignUserRole,
|
||||||
getSystemRoles,
|
getSystemRoles,
|
||||||
getCurrentUserTenantId,
|
getCurrentUserTenantId,
|
||||||
|
getTenantUsers,
|
||||||
notifyNewUserCreated,
|
notifyNewUserCreated,
|
||||||
} from '../services/api';
|
} from '../services/api';
|
||||||
import { Modal } from '../components/ui/Modal';
|
import { Modal } from '../components/ui/Modal';
|
||||||
@@ -77,6 +78,8 @@ import { FundingInstrumentsSection, InstructionsSection } from '../components/go
|
|||||||
import { RulesSection } from '../components/governance/RulesSection';
|
import { RulesSection } from '../components/governance/RulesSection';
|
||||||
import {
|
import {
|
||||||
Organisation,
|
Organisation,
|
||||||
|
TenantOrganisation,
|
||||||
|
TenantUser,
|
||||||
Portfolio,
|
Portfolio,
|
||||||
Programme,
|
Programme,
|
||||||
Instrument,
|
Instrument,
|
||||||
@@ -137,8 +140,8 @@ type ProgrammeFormValues = z.infer<typeof programmeSchema>;
|
|||||||
|
|
||||||
export const GovernancePage: React.FC = () => {
|
export const GovernancePage: React.FC = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { sub, user, claims } = useAuth();
|
const { sub, user, claims, roles, tenantId } = useAuth();
|
||||||
const { canAdd, canApprove, canExport, canApproveRecord, isMaker } = useRbac();
|
const { canAdd, canApprove, canExport, canApproveRecord, isMaker, isUnrestricted } = useRbac();
|
||||||
const currentUserId = sub || user?.id || '';
|
const currentUserId = sub || user?.id || '';
|
||||||
const { success, error: toastError } = useToast();
|
const { success, error: toastError } = useToast();
|
||||||
|
|
||||||
@@ -265,6 +268,94 @@ export const GovernancePage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}, [usersDirectory]);
|
}, [usersDirectory]);
|
||||||
|
|
||||||
|
const isAdmin =
|
||||||
|
Boolean(isUnrestricted) ||
|
||||||
|
(Array.isArray(roles) && roles.some((r) => r.toLowerCase().trim() === 'admin')) ||
|
||||||
|
(claims?.role && String(claims.role).toLowerCase().includes('admin'));
|
||||||
|
|
||||||
|
const orgTenantId = editingOrg?.tenantId || tenantId || getCurrentUserTenantId() || 1;
|
||||||
|
|
||||||
|
const { data: tenantUsersList = [] } = useQuery<TenantUser[]>({
|
||||||
|
queryKey: ['tenant-users', orgTenantId],
|
||||||
|
queryFn: () => getTenantUsers(orgTenantId),
|
||||||
|
enabled: isOrgModalOpen && !!editingOrg && !isAdmin,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tenantUsersList.length > 0) {
|
||||||
|
tenantUsersList.forEach((tu) => {
|
||||||
|
if (tu.userId && tu.user?.email) {
|
||||||
|
registerUserEmail(tu.userId, tu.user.email);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [tenantUsersList]);
|
||||||
|
|
||||||
|
const modalExecutiveUsers = React.useMemo(() => {
|
||||||
|
let list: User[] = [];
|
||||||
|
|
||||||
|
if (isAdmin) {
|
||||||
|
list = usersDirectory.filter((u) => String(u.role || '').toLowerCase() === 'executive');
|
||||||
|
} else {
|
||||||
|
list = tenantUsersList
|
||||||
|
.map((tu) => tu.user || ({ id: tu.userId, email: getUserEmail(tu.userId), role: 'Executive' } as User))
|
||||||
|
.filter((u) => String(u.role || '').toLowerCase() === 'executive');
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentLinkedUserId =
|
||||||
|
editingOrg?.userId ||
|
||||||
|
editingOrg?.UserId ||
|
||||||
|
editingOrg?.organisationUser ||
|
||||||
|
editingOrg?.OrganisationUser ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
if (currentLinkedUserId) {
|
||||||
|
const alreadyIncluded = list.some(
|
||||||
|
(u) => u.id && String(u.id).toLowerCase() === String(currentLinkedUserId).toLowerCase()
|
||||||
|
);
|
||||||
|
if (!alreadyIncluded) {
|
||||||
|
const resolvedEmail = getUserEmail(currentLinkedUserId);
|
||||||
|
const matchedInDir = usersDirectory.find(
|
||||||
|
(u) => u.id && String(u.id).toLowerCase() === String(currentLinkedUserId).toLowerCase()
|
||||||
|
);
|
||||||
|
list = [
|
||||||
|
{
|
||||||
|
id: currentLinkedUserId,
|
||||||
|
email: matchedInDir?.email || (resolvedEmail !== '—' && resolvedEmail ? resolvedEmail : currentLinkedUserId),
|
||||||
|
role: 'Executive',
|
||||||
|
status: ActivityStatus.Active,
|
||||||
|
lastLoginAt: null,
|
||||||
|
},
|
||||||
|
...list,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}, [isAdmin, usersDirectory, tenantUsersList, editingOrg]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOrgModalOpen && editingOrg) {
|
||||||
|
const targetUserId =
|
||||||
|
editingOrg.userId ||
|
||||||
|
editingOrg.UserId ||
|
||||||
|
editingOrg.organisationUser ||
|
||||||
|
editingOrg.OrganisationUser ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
if (targetUserId) {
|
||||||
|
const targetEmail = getUserEmail(targetUserId);
|
||||||
|
const matched = modalExecutiveUsers.find(
|
||||||
|
(u) =>
|
||||||
|
(u.id && String(u.id).toLowerCase() === String(targetUserId).toLowerCase()) ||
|
||||||
|
(u.email && targetEmail && targetEmail !== '—' && u.email.toLowerCase() === targetEmail.toLowerCase())
|
||||||
|
);
|
||||||
|
const resolvedId = matched?.id || targetUserId;
|
||||||
|
orgForm.setValue('organisationUser', resolvedId, { shouldValidate: true, shouldDirty: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isOrgModalOpen, editingOrg, modalExecutiveUsers]);
|
||||||
|
|
||||||
// Quick Add Executive User Subform state
|
// Quick Add Executive User Subform state
|
||||||
const [isQuickAddUserOpen, setIsQuickAddUserOpen] = useState(false);
|
const [isQuickAddUserOpen, setIsQuickAddUserOpen] = useState(false);
|
||||||
const [quickUserEmail, setQuickUserEmail] = useState('');
|
const [quickUserEmail, setQuickUserEmail] = useState('');
|
||||||
@@ -597,6 +688,7 @@ export const GovernancePage: React.FC = () => {
|
|||||||
registrationNo: data.registrationNo,
|
registrationNo: data.registrationNo,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
organisationUser: data.organisationUser,
|
organisationUser: data.organisationUser,
|
||||||
|
userId: data.organisationUser,
|
||||||
type: Number(data.type),
|
type: Number(data.type),
|
||||||
status: data.status !== undefined ? Number(data.status) : ActivityStatus.Active,
|
status: data.status !== undefined ? Number(data.status) : ActivityStatus.Active,
|
||||||
});
|
});
|
||||||
@@ -741,8 +833,19 @@ export const GovernancePage: React.FC = () => {
|
|||||||
org.createdBy ||
|
org.createdBy ||
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
if (targetUserId) {
|
||||||
|
const email = getUserEmail(targetUserId);
|
||||||
|
if (email && email !== '—') {
|
||||||
|
registerUserEmail(targetUserId, email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const matchedUser = targetUserId
|
const matchedUser = targetUserId
|
||||||
? usersDirectory.find((u) => u.id && String(u.id).toLowerCase() === String(targetUserId).toLowerCase())
|
? usersDirectory.find(
|
||||||
|
(u) =>
|
||||||
|
(u.id && String(u.id).toLowerCase() === String(targetUserId).toLowerCase()) ||
|
||||||
|
(u.email && getUserEmail(targetUserId) && u.email.toLowerCase() === getUserEmail(targetUserId).toLowerCase())
|
||||||
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const selectedUserId = matchedUser?.id || targetUserId || '';
|
const selectedUserId = matchedUser?.id || targetUserId || '';
|
||||||
@@ -1714,7 +1817,7 @@ export const GovernancePage: React.FC = () => {
|
|||||||
className="w-full px-3 py-2 text-xs border border-slate-700/80 rounded-xl focus:outline-none focus:ring-1 focus:ring-blue-500 bg-[#090d16] text-white font-medium"
|
className="w-full px-3 py-2 text-xs border border-slate-700/80 rounded-xl focus:outline-none focus:ring-1 focus:ring-blue-500 bg-[#090d16] text-white font-medium"
|
||||||
>
|
>
|
||||||
<option value="">-- Select linked executive or manager account --</option>
|
<option value="">-- Select linked executive or manager account --</option>
|
||||||
{executiveAndManagerUsers.map((u) => (
|
{(editingOrg ? modalExecutiveUsers : executiveAndManagerUsers).map((u) => (
|
||||||
<option key={u.id} value={u.id}>
|
<option key={u.id} value={u.id}>
|
||||||
{u.email || u.id} ({u.role || 'Executive'})
|
{u.email || u.id} ({u.role || 'Executive'})
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ export const createOrganisation = async (payload: CreateOrganisationRequest): Pr
|
|||||||
email: payload.email,
|
email: payload.email,
|
||||||
idNumber: payload.idNumber || null,
|
idNumber: payload.idNumber || null,
|
||||||
organisationUser: payload.organisationUser,
|
organisationUser: payload.organisationUser,
|
||||||
|
userId: payload.userId || payload.organisationUser,
|
||||||
type: payload.type !== undefined ? Number(payload.type) : 5,
|
type: payload.type !== undefined ? Number(payload.type) : 5,
|
||||||
});
|
});
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -261,6 +262,7 @@ export const updateOrganisation = async (payload: UpdateOrganisationRequest): Pr
|
|||||||
email: payload.email,
|
email: payload.email,
|
||||||
idNumber: payload.idNumber,
|
idNumber: payload.idNumber,
|
||||||
organisationUser: payload.organisationUser,
|
organisationUser: payload.organisationUser,
|
||||||
|
userId: payload.userId || payload.organisationUser,
|
||||||
type: Number(payload.type),
|
type: Number(payload.type),
|
||||||
status: orgStatus,
|
status: orgStatus,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -388,6 +388,7 @@ export interface CreateOrganisationRequest {
|
|||||||
email: string;
|
email: string;
|
||||||
idNumber?: string | null;
|
idNumber?: string | null;
|
||||||
organisationUser: string;
|
organisationUser: string;
|
||||||
|
userId?: string | null;
|
||||||
type: OrganisationType;
|
type: OrganisationType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +399,7 @@ export interface UpdateOrganisationRequest {
|
|||||||
email: string;
|
email: string;
|
||||||
idNumber?: string | null;
|
idNumber?: string | null;
|
||||||
organisationUser: string;
|
organisationUser: string;
|
||||||
|
userId?: string | null;
|
||||||
type: OrganisationType;
|
type: OrganisationType;
|
||||||
status?: ActivityStatus;
|
status?: ActivityStatus;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user