Fixed idNumber missed assignment when updating organisation

This commit is contained in:
2026-10-25 13:34:52 +02:00
parent c8e54815ef
commit 4e1211a1e1
@@ -29,6 +29,7 @@ import {
CheckCircle2,
Loader2,
Lock,
AlertTriangle,
} from 'lucide-react';
import {
getOrganisations,
@@ -113,6 +114,7 @@ const organisationSchema = z.object({
registrationNo: z.string().min(4, 'Registration number is mandatory'),
email: z.string().email('Enter a valid email address'),
organisationUser: z.string().min(1, 'Please select a linked executive or manager user'),
idNumber: z.string().optional(),
type: z.coerce.number().min(1).max(27),
status: z.coerce.number().optional(),
});
@@ -593,11 +595,23 @@ export const GovernancePage: React.FC = () => {
registrationNo: '',
email: '',
organisationUser: '',
idNumber: '',
type: OrganisationType.Enterprise,
status: ActivityStatus.Active,
},
});
const watchedOrgUser = orgForm.watch('organisationUser');
const selectedExecutiveUser = React.useMemo(() => {
if (!watchedOrgUser) return null;
const str = String(watchedOrgUser).toLowerCase();
return (
usersDirectory.find((u) => u.id && String(u.id).toLowerCase() === str) ||
modalExecutiveUsers.find((u) => u.id && String(u.id).toLowerCase() === str) ||
null
);
}, [watchedOrgUser, usersDirectory, modalExecutiveUsers]);
const portfolioForm = useForm<PortfolioFormValues>({
resolver: zodResolver(portfolioSchema),
defaultValues: {
@@ -622,11 +636,20 @@ export const GovernancePage: React.FC = () => {
const orgMutation = useMutation({
mutationFn: (data: OrganisationFormValues) => {
if (editingOrg) {
if (!data.idNumber || !data.idNumber.trim()) {
orgForm.setError('idNumber', {
type: 'manual',
message: 'Government ID number is required to assign this executive user.',
});
throw new Error('Executive user ID number is required when updating an organisation.');
}
return updateOrganisation({
organisationId: editingOrg.id,
name: data.name,
registrationNo: data.registrationNo,
email: data.email,
idNumber: data.idNumber.trim(),
organisationUser: data.organisationUser,
type: Number(data.type),
status: data.status !== undefined ? Number(data.status) : ActivityStatus.Active,
@@ -636,6 +659,7 @@ export const GovernancePage: React.FC = () => {
name: data.name,
registrationNo: data.registrationNo,
email: data.email,
idNumber: data.idNumber?.trim() || null,
organisationUser: data.organisationUser,
type: Number(data.type),
});
@@ -753,6 +777,7 @@ export const GovernancePage: React.FC = () => {
registrationNo: '',
email: '',
organisationUser: defaultUser?.id || '',
idNumber: defaultUser?.idNumber || '',
type: OrganisationType.Enterprise,
status: ActivityStatus.Active,
});
@@ -770,9 +795,10 @@ export const GovernancePage: React.FC = () => {
'';
let selectedUserId = '';
let matchedUser: User | undefined;
if (targetUserId) {
const targetStr = String(targetUserId).toLowerCase();
const matchedUser = usersDirectory.find(
matchedUser = usersDirectory.find(
(u) =>
(u.id && String(u.id).toLowerCase() === targetStr) ||
(u.email && u.email.toLowerCase() === targetStr)
@@ -792,15 +818,25 @@ export const GovernancePage: React.FC = () => {
}
}
const resolvedIdNumber = matchedUser?.idNumber || org.idNumber || '';
orgForm.reset({
name: org.name || '',
registrationNo: org.registrationNo || '',
email: org.email || '',
organisationUser: selectedUserId,
idNumber: resolvedIdNumber,
type: org.type || OrganisationType.Enterprise,
status: org.status ?? ActivityStatus.Active,
});
if (selectedUserId && !resolvedIdNumber) {
orgForm.setError('idNumber', {
type: 'manual',
message: 'This executive user has no ID number on file. An ID number is required to update the organisation.',
});
}
setIsQuickAddUserOpen(false);
setQuickUserError(null);
setIsOrgModalOpen(true);
@@ -1760,7 +1796,32 @@ export const GovernancePage: React.FC = () => {
)}
</div>
<select
{...orgForm.register('organisationUser')}
{...orgForm.register('organisationUser', {
onChange: (e) => {
const selectedId = e.target.value;
if (!selectedId) {
orgForm.setValue('idNumber', '');
return;
}
const str = String(selectedId).toLowerCase();
const matched =
usersDirectory.find((u) => u.id && String(u.id).toLowerCase() === str) ||
modalExecutiveUsers.find((u) => u.id && String(u.id).toLowerCase() === str);
if (matched?.idNumber && matched.idNumber.trim()) {
orgForm.setValue('idNumber', matched.idNumber.trim(), { shouldValidate: true });
orgForm.clearErrors('idNumber');
} else {
orgForm.setValue('idNumber', '', { shouldValidate: true });
if (editingOrg) {
orgForm.setError('idNumber', {
type: 'manual',
message: 'Selected user has no ID number on file. Please enter their ID number to enable executive assignment.',
});
}
}
},
})}
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>
@@ -1778,9 +1839,61 @@ export const GovernancePage: React.FC = () => {
No Executive or Manager user found. Create one below to continue.
</p>
)}
{watchedOrgUser && selectedExecutiveUser && selectedExecutiveUser.idNumber && selectedExecutiveUser.idNumber.trim() && (
<div className="flex items-center gap-1.5 text-[11px] text-emerald-400 mt-1">
<CheckCircle className="w-3.5 h-3.5 text-emerald-400 shrink-0" />
<span>ID on file: <strong className="font-mono text-emerald-300">{selectedExecutiveUser.idNumber}</strong></span>
</div>
)}
</div>
</div>
{/* Missing ID Number Alert */}
{watchedOrgUser && selectedExecutiveUser && (!selectedExecutiveUser.idNumber || !selectedExecutiveUser.idNumber.trim()) && (
<div className="p-3 rounded-xl border border-amber-500/30 bg-amber-500/10 text-amber-300 text-xs flex items-start gap-2.5">
<AlertTriangle className="w-4 h-4 text-amber-400 shrink-0 mt-0.5" />
<div className="space-y-1">
<p className="font-semibold text-amber-200">
Missing Executive ID Number
</p>
<p className="text-[11px] text-amber-300/90 leading-relaxed">
The selected executive (<strong className="font-mono text-white">{selectedExecutiveUser.email || watchedOrgUser}</strong>) does not have a Government ID Number on file. An ID number is required to assign this executive user; otherwise the update will be ignored. Please enter their ID number below.
</p>
</div>
</div>
)}
{/* Executive User ID Number Input */}
<div>
<div className="flex items-center justify-between mb-1">
<div className="flex items-center gap-1">
<label className="block text-xs font-semibold text-slate-300">
Executive ID Number {editingOrg ? '*' : '(Optional)'}
</label>
<HelpTooltip
title="Executive ID Requirement"
content="Government Issued ID or Passport of the executive. Required when updating to persist the executive user link; otherwise the update will be ignored."
size="sm"
placement="top"
/>
</div>
{selectedExecutiveUser?.idNumber && (
<span className="text-[10px] text-emerald-400 font-mono">Sourced from profile</span>
)}
</div>
<input
type="text"
{...orgForm.register('idNumber')}
className={`w-full px-3 py-2 text-xs border rounded-xl focus:outline-none focus:ring-1 focus:ring-blue-500 text-white bg-[#090d16] font-mono ${
orgForm.formState.errors.idNumber ? 'border-rose-500 ring-1 ring-rose-500' : 'border-slate-700/80'
}`}
placeholder="Enter 13-digit ID or Passport Number"
/>
{orgForm.formState.errors.idNumber && (
<p className="text-[10px] text-rose-500 mt-1">{orgForm.formState.errors.idNumber.message}</p>
)}
</div>
{/* Inline Collapsible Subform to Create Executive User */}
{isQuickAddUserOpen && !editingOrg && (
<div className="p-3.5 rounded-xl border border-blue-500/30 bg-blue-950/20 space-y-3">