Merge pull request 'Refactored org modal logic' (#135) from finishline into main

Reviewed-on: #135
This commit was merged in pull request #135.
This commit is contained in:
2026-10-04 18:22:13 +02:00
@@ -551,7 +551,7 @@ export const GovernancePage: React.FC = () => {
const openCreateOrg = () => { const openCreateOrg = () => {
setEditingOrg(null); setEditingOrg(null);
const defaultUserId = usersDirectory[0]?.id || sub || currentUserId; const defaultUserId = usersDirectory[0]?.id || sub || currentUserId || '';
const defaultUser = usersDirectory.find((u) => u.id === defaultUserId); const defaultUser = usersDirectory.find((u) => u.id === defaultUserId);
orgForm.reset({ orgForm.reset({
name: '', name: '',
@@ -565,70 +565,58 @@ export const GovernancePage: React.FC = () => {
setIsOrgModalOpen(true); setIsOrgModalOpen(true);
}; };
const openEditOrg = async (org: Organisation) => { const openEditOrg = (org: Organisation) => {
setEditingOrg(org); setEditingOrg(org);
setIsOrgModalOpen(true);
// 1. Fetch fresh organisation details from API // 1. Resolve target linked user ID synchronously from the organisation model
let currentOrg = org;
try {
const fresh = await getOrganisation(org.id);
if (fresh) {
currentOrg = fresh;
}
} catch {
// fallback to passed org
}
// 2. Resolve linked user ID from organisation model
const targetUserId = const targetUserId =
currentOrg.userId || org.userId ||
(currentOrg as any).UserId || (org as any).UserId ||
(currentOrg as any).organisationUser || (org as any).organisationUser ||
(currentOrg as any).OrganisationUser || (org as any).OrganisationUser ||
currentOrg.createdBy || org.createdBy ||
''; '';
// 3. Find user in directory or fetch user record to get cached user profile and idNumber // 2. Fast O(1) in-memory match from cached users directory
let matchedUser = usersDirectory.find( const matchedUser = targetUserId
(u) => u.id && String(u.id).toLowerCase() === String(targetUserId).toLowerCase() ? usersDirectory.find((u) => u.id && String(u.id).toLowerCase() === String(targetUserId).toLowerCase())
); : null;
if (!matchedUser && targetUserId) { // 3. Resolve idNumber: prefer matchedUser's idNumber, or org's idNumber if any, otherwise blank ''
try { const resolvedIdNumber = matchedUser?.idNumber || org.idNumber || (org as any).IdNumber || '';
const fetchedUser = await getUser(targetUserId); const selectedUserId = matchedUser?.id || targetUserId || '';
if (fetchedUser) {
matchedUser = fetchedUser;
}
} catch {
// user fetch failed
}
}
// 4. Ensure matched user is present in usersDirectory cache for dropdown rendering
if (matchedUser && !usersDirectory.some((u) => u.id === matchedUser!.id)) {
queryClient.setQueryData<User[]>(['users-directory'], (old = []) => [matchedUser!, ...old]);
}
// 5. Resolve idNumber from matched user profile or organisation
const resolvedIdNumber =
matchedUser?.idNumber ||
currentOrg.idNumber ||
(currentOrg as any).IdNumber ||
(org as any).idNumber ||
'';
const selectedUserId = matchedUser?.id || targetUserId || usersDirectory[0]?.id || currentUserId;
// 4. Perform assignment immediately to form state
orgForm.reset({ orgForm.reset({
name: currentOrg.name || '', name: org.name || '',
registrationNo: currentOrg.registrationNo || '', registrationNo: org.registrationNo || '',
email: currentOrg.email || '', email: org.email || '',
idNumber: resolvedIdNumber, idNumber: resolvedIdNumber,
organisationUser: selectedUserId, organisationUser: selectedUserId,
type: currentOrg.type || OrganisationType.Enterprise, type: org.type || OrganisationType.Enterprise,
status: currentOrg.status ?? ActivityStatus.Active, status: org.status ?? ActivityStatus.Active,
}); });
// 5. Instantly open modal with fully populated data (no lag, no progressive pop-in)
setIsOrgModalOpen(true);
// 6. Non-blocking background sync if target user wasn't in cache or org has missing data
if (targetUserId && !matchedUser) {
getUser(targetUserId).then((fetchedUser) => {
if (fetchedUser) {
queryClient.setQueryData<User[]>(['users-directory'], (old = []) => {
if (old.some((u) => u.id === fetchedUser.id)) return old;
return [fetchedUser, ...old];
});
if (fetchedUser.idNumber && !orgForm.getValues('idNumber')) {
orgForm.setValue('idNumber', fetchedUser.idNumber, { shouldValidate: true });
}
if (!orgForm.getValues('organisationUser')) {
orgForm.setValue('organisationUser', fetchedUser.id, { shouldValidate: true });
}
}
}).catch(() => {});
}
}; };
const openCreatePortfolio = () => { const openCreatePortfolio = () => {
@@ -1499,23 +1487,16 @@ export const GovernancePage: React.FC = () => {
</div> </div>
<select <select
{...orgForm.register('organisationUser', { {...orgForm.register('organisationUser', {
onChange: async (e) => { onChange: (e) => {
const selectedId = e.target.value; const selectedId = e.target.value;
let selected = usersDirectory.find((u) => u.id === selectedId); const selected = usersDirectory.find((u) => u.id === selectedId);
if (!selected && selectedId) { // Populate selected user's ID number or blank '' if deselected or missing
try { orgForm.setValue('idNumber', selected?.idNumber || '', { shouldValidate: true });
selected = await getUser(selectedId);
} catch {
// ignore
}
}
if (selected?.idNumber) {
orgForm.setValue('idNumber', selected.idNumber, { shouldValidate: true });
}
}, },
})} })}
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 user account --</option>
{usersDirectory.map((u) => ( {usersDirectory.map((u) => (
<option key={u.id} value={u.id}> <option key={u.id} value={u.id}>
{u.email || u.id} {u.role ? `(${u.role})` : ''} {u.idNumber ? `• ID: ${u.idNumber}` : ''} {u.email || u.id} {u.role ? `(${u.role})` : ''} {u.idNumber ? `• ID: ${u.idNumber}` : ''}