Fix nurse onboarding redirects and specialty Documents 404s.
Deploy Ladill Care / deploy (push) Successful in 36s

Resolve staff memberships by invite/demo email as well as public_id so nurses stay on their employer org; block staff from owner onboarding; keep Documents viewable with module access while upload stays manage-gated.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-18 19:09:06 +00:00
co-authored by Cursor
parent 656f402e8f
commit c3219a1bf1
7 changed files with 524 additions and 14 deletions
+80 -5
View File
@@ -17,9 +17,7 @@ class OrganizationResolver
{
public function resolveForUser(User $user): ?Organization
{
$ref = $user->ownerRef();
$member = Member::where('user_ref', $ref)->first();
$member = $this->membershipFor($user);
if ($member) {
$organization = Organization::query()->find($member->organization_id);
if ($organization) {
@@ -27,7 +25,7 @@ class OrganizationResolver
}
}
return Organization::owned($ref)->first();
return Organization::owned($user->ownerRef())->first();
}
public function forUser(User $user): Organization
@@ -44,6 +42,40 @@ class OrganizationResolver
&& (bool) data_get($organization->settings, 'onboarded', false);
}
/**
* Any Care membership for this user (public_id or invite/demo email key).
* Remaps email-keyed rows to public_id when found so staff stay linked after SSO.
*/
public function membershipFor(User $user): ?Member
{
$member = Member::query()->where('user_ref', $user->ownerRef())->first();
if ($member) {
return $member;
}
$email = strtolower(trim((string) $user->email));
if ($email === '' || $email === $user->ownerRef()) {
return null;
}
$member = Member::query()->where('user_ref', $email)->first();
return $member ? $this->remapMemberUserRef($member, $user) : null;
}
/**
* True when the user may run owner onboarding (create a facility).
* Staff already attached to a tenant must not create a second org.
*/
public function canCompleteOnboarding(User $user): bool
{
if ($this->membershipFor($user)) {
return false;
}
return ! $this->isOnboarded($user);
}
public function memberFor(User $user, ?Organization $organization = null): ?Member
{
$organization ??= $this->resolveForUser($user);
@@ -51,9 +83,52 @@ class OrganizationResolver
return null;
}
return Member::where('organization_id', $organization->id)
$member = Member::query()
->where('organization_id', $organization->id)
->where('user_ref', $user->ownerRef())
->first();
if ($member) {
return $member;
}
$email = strtolower(trim((string) $user->email));
if ($email === '') {
return null;
}
$member = Member::query()
->where('organization_id', $organization->id)
->where('user_ref', $email)
->first();
return $member ? $this->remapMemberUserRef($member, $user) : null;
}
/**
* Promote invite/demo email user_ref keys to the stable OIDC public_id.
*/
protected function remapMemberUserRef(Member $member, User $user): Member
{
$ref = $user->ownerRef();
if ($member->user_ref === $ref) {
return $member;
}
$conflict = Member::query()
->where('organization_id', $member->organization_id)
->where('user_ref', $ref)
->where('id', '!=', $member->id)
->first();
if ($conflict) {
$member->delete();
return $conflict;
}
$member->forceFill(['user_ref' => $ref])->save();
return $member->fresh() ?? $member;
}
public function ensureOwnerMember(User $user, Organization $organization): Member