Fine tuned organisation onboarding and tenant management

This commit is contained in:
2026-10-04 10:34:59 +02:00
parent 5fb67df9ac
commit 681ed99fea
5 changed files with 407 additions and 261 deletions
@@ -94,6 +94,8 @@ const organisationSchema = z.object({
name: z.string().min(3, 'Name must be at least 3 characters'),
registrationNo: z.string().min(4, 'Registration number is mandatory'),
email: z.string().email('Enter a valid email address'),
idNumber: z.string().min(4, 'Government issued ID number is required'),
organisationUser: z.string().min(1, 'Please select a linked user'),
type: z.coerce.number().min(1).max(27),
status: z.coerce.number().optional(),
});
@@ -422,6 +424,8 @@ export const GovernancePage: React.FC = () => {
name: '',
registrationNo: '',
email: '',
idNumber: '',
organisationUser: '',
type: OrganisationType.Enterprise,
status: ActivityStatus.Active,
},
@@ -456,11 +460,20 @@ export const GovernancePage: React.FC = () => {
name: data.name,
registrationNo: data.registrationNo,
email: data.email,
idNumber: data.idNumber,
organisationUser: data.organisationUser,
type: Number(data.type),
status: data.status !== undefined ? Number(data.status) : undefined,
status: data.status !== undefined ? Number(data.status) : ActivityStatus.Active,
});
}
return createOrganisation(data as CreateOrganisationRequest);
return createOrganisation({
name: data.name,
registrationNo: data.registrationNo,
email: data.email,
idNumber: data.idNumber,
organisationUser: data.organisationUser,
type: Number(data.type),
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['organisations'] });
@@ -537,10 +550,14 @@ export const GovernancePage: React.FC = () => {
const openCreateOrg = () => {
setEditingOrg(null);
const defaultUserId = usersDirectory[0]?.id || sub || currentUserId;
const defaultUser = usersDirectory.find((u) => u.id === defaultUserId);
orgForm.reset({
name: '',
registrationNo: '',
email: '',
idNumber: defaultUser?.idNumber || '',
organisationUser: defaultUserId,
type: OrganisationType.Enterprise,
status: ActivityStatus.Active,
});
@@ -549,10 +566,14 @@ export const GovernancePage: React.FC = () => {
const openEditOrg = (org: Organisation) => {
setEditingOrg(org);
const linkedUserId = org.userId || org.createdBy || usersDirectory[0]?.id || currentUserId;
const matchedUser = usersDirectory.find((u) => u.id === linkedUserId);
orgForm.reset({
name: org.name || '',
registrationNo: org.registrationNo || '',
email: org.email || '',
idNumber: matchedUser?.idNumber || (org as any).idNumber || '',
organisationUser: linkedUserId,
type: org.type || OrganisationType.Enterprise,
status: org.status ?? ActivityStatus.Active,
});
@@ -1412,6 +1433,65 @@ export const GovernancePage: React.FC = () => {
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<div className="flex items-center gap-1 mb-1">
<label className="block text-xs font-semibold text-slate-300">
Linked User Account *
</label>
<HelpTooltip
title="Designated Organisation User"
content="Select the designated user account associated with this organisation profile."
size="sm"
placement="top"
/>
</div>
<select
{...orgForm.register('organisationUser', {
onChange: (e) => {
const selected = usersDirectory.find((u) => u.id === e.target.value);
if (selected?.idNumber && !orgForm.getValues('idNumber')) {
orgForm.setValue('idNumber', selected.idNumber);
}
},
})}
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"
>
{usersDirectory.map((u) => (
<option key={u.id} value={u.id}>
{u.email || u.id} {u.role ? `(${u.role})` : ''} {u.idNumber ? `• ID: ${u.idNumber}` : ''}
</option>
))}
</select>
{orgForm.formState.errors.organisationUser && (
<p className="text-[10px] text-rose-500 mt-1">{orgForm.formState.errors.organisationUser.message}</p>
)}
</div>
<div>
<div className="flex items-center gap-1 mb-1">
<label className="block text-xs font-semibold text-slate-300">
Government Issued ID Number *
</label>
<HelpTooltip
title="Government Issued ID"
content="National identification document number or statutory passport ID of the linked user."
size="sm"
placement="top"
/>
</div>
<input
type="text"
{...orgForm.register('idNumber')}
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 font-mono text-white bg-[#090d16]"
placeholder="e.g. 8401015800085 or Passport Number"
/>
{orgForm.formState.errors.idNumber && (
<p className="text-[10px] text-rose-500 mt-1">{orgForm.formState.errors.idNumber.message}</p>
)}
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<div className="flex items-center gap-1 mb-1">
@@ -2103,6 +2183,12 @@ export const GovernancePage: React.FC = () => {
<span className="text-slate-400 font-semibold text-[11px] uppercase tracking-wider shrink-0">Entity Type:</span>
<span className="text-slate-300">{getOrganisationTypeLabel(orgFreshDetail?.type ?? selectedOrgForDetail?.type ?? OrganisationType.Enterprise)}</span>
</div>
<div className="flex items-center gap-2 min-w-0">
<span className="text-slate-400 font-semibold text-[11px] uppercase tracking-wider shrink-0">Linked User:</span>
<span className="text-blue-400 font-mono text-[11px] truncate">
{getUserEmail(orgFreshDetail?.userId || selectedOrgForDetail?.userId) || orgFreshDetail?.userId || selectedOrgForDetail?.userId || '—'}
</span>
</div>
</div>
</div>