Initial Ladill Frontdesk release with deploy pipeline.
Visitor management app with SSO, kiosk, badges, reports, and Gitea CI deploy to frontdesk.ladill.com. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Frontdesk;
|
||||
|
||||
use App\Models\Branch;
|
||||
use App\Models\Host;
|
||||
use App\Models\Member;
|
||||
use App\Models\Organization;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class OrganizationResolver
|
||||
{
|
||||
public function resolveForUser(User $user): ?Organization
|
||||
{
|
||||
$ref = $user->ownerRef();
|
||||
|
||||
$member = Member::where('user_ref', $ref)->first();
|
||||
if ($member) {
|
||||
return Organization::find($member->organization_id);
|
||||
}
|
||||
|
||||
return Organization::owned($ref)->first();
|
||||
}
|
||||
|
||||
public function forUser(User $user): Organization
|
||||
{
|
||||
return $this->resolveForUser($user)
|
||||
?? throw new \RuntimeException('No organization for user.');
|
||||
}
|
||||
|
||||
public function isOnboarded(User $user): bool
|
||||
{
|
||||
$organization = $this->resolveForUser($user);
|
||||
|
||||
return $organization !== null
|
||||
&& (bool) data_get($organization->settings, 'onboarded', false);
|
||||
}
|
||||
|
||||
public function memberFor(User $user, ?Organization $organization = null): ?Member
|
||||
{
|
||||
$organization ??= $this->resolveForUser($user);
|
||||
if (! $organization) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Member::where('organization_id', $organization->id)
|
||||
->where('user_ref', $user->ownerRef())
|
||||
->first();
|
||||
}
|
||||
|
||||
public function ensureOwnerMember(User $user, Organization $organization): Member
|
||||
{
|
||||
return Member::firstOrCreate(
|
||||
[
|
||||
'organization_id' => $organization->id,
|
||||
'user_ref' => $user->ownerRef(),
|
||||
],
|
||||
[
|
||||
'owner_ref' => $user->ownerRef(),
|
||||
'role' => 'org_admin',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
public function completeOnboarding(User $user, array $data): Organization
|
||||
{
|
||||
$ref = $user->ownerRef();
|
||||
|
||||
$organization = Organization::create([
|
||||
'owner_ref' => $ref,
|
||||
'name' => $data['organization_name'],
|
||||
'slug' => Str::slug($data['organization_name']).'-'.substr($ref, 0, 6),
|
||||
'timezone' => $data['timezone'] ?? config('app.timezone', 'UTC'),
|
||||
'settings' => [
|
||||
'onboarded' => true,
|
||||
'badge_expiry_hours' => (int) ($data['badge_expiry_hours'] ?? config('frontdesk.badge.default_expiry_hours', 8)),
|
||||
'kiosk_reset_seconds' => (int) ($data['kiosk_reset_seconds'] ?? config('frontdesk.kiosk.inactivity_reset_seconds', 120)),
|
||||
'notification_channels' => ['email'],
|
||||
'visitor_policy' => $data['visitor_policy'] ?? null,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->ensureOwnerMember($user, $organization);
|
||||
|
||||
Branch::create([
|
||||
'owner_ref' => $ref,
|
||||
'organization_id' => $organization->id,
|
||||
'name' => $data['branch_name'],
|
||||
'address' => $data['branch_address'] ?? null,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
return $organization;
|
||||
}
|
||||
|
||||
public function hostFor(User $user): ?Host
|
||||
{
|
||||
$organization = $this->resolveForUser($user);
|
||||
if (! $organization) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Host::where('organization_id', $organization->id)
|
||||
->where('user_ref', $user->ownerRef())
|
||||
->first();
|
||||
}
|
||||
|
||||
/** Branch ID the member may access; null = all branches. */
|
||||
public function branchScope(?Member $member): ?int
|
||||
{
|
||||
if ($member === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (in_array($member->role, ['super_admin', 'org_admin', 'auditor'], true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $member->branch_id;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user