From acceff6d3670320d0b4447b40f942aa1261b7c74 Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 27 Jan 2026 00:31:43 +0000 Subject: [PATCH] monorepo sepration --- .github/package-workflows/README.md | 62 --- .github/package-workflows/ci.yml | 55 --- .github/package-workflows/release.yml | 40 -- Boot.php | 56 +++ README.md | 2 +- Routes/admin.php | 24 ++ View/Blade/admin/member-manager.blade.php | 370 ++++++++++++++++ View/Blade/admin/team-manager.blade.php | 276 ++++++++++++ View/Modal/Admin/MemberManager.php | 404 ++++++++++++++++++ View/Modal/Admin/TeamManager.php | 289 +++++++++++++ composer.json | 42 +- phpunit.xml | 33 -- src/Boot.php | 46 +- src/Concerns/BelongsToNamespace.php | 6 +- src/Concerns/BelongsToWorkspace.php | 10 +- src/Concerns/HasWorkspaceCache.php | 6 +- src/Concerns/TwoFactorAuthenticatable.php | 8 +- src/Console/Commands/CheckUsageAlerts.php | 6 +- .../Commands/ProcessAccountDeletions.php | 4 +- src/Console/Commands/RefreshUserStats.php | 6 +- src/Console/Commands/ResetBillingCycles.php | 14 +- src/Contracts/EntitlementWebhookEvent.php | 2 +- .../TwoFactorAuthenticationProvider.php | 2 +- .../Api/EntitlementWebhookController.php | 10 +- src/Controllers/EntitlementApiController.php | 14 +- src/Controllers/ReferralController.php | 2 +- src/Controllers/WorkspaceController.php | 6 +- .../WorkspaceInvitationController.php | 6 +- src/Database/Factories/UserFactory.php | 6 +- src/Database/Factories/UserTokenFactory.php | 6 +- .../Factories/WaitlistEntryFactory.php | 6 +- src/Database/Factories/WorkspaceFactory.php | 6 +- .../Factories/WorkspaceInvitationFactory.php | 6 +- src/Database/Seeders/DemoTestUserSeeder.php | 8 +- src/Database/Seeders/DemoWorkspaceSeeder.php | 12 +- src/Database/Seeders/FeatureSeeder.php | 4 +- .../Seeders/SystemWorkspaceSeeder.php | 8 +- src/Database/Seeders/WorkspaceSeeder.php | 12 +- src/Enums/UserTier.php | 2 +- src/Enums/WebhookDeliveryStatus.php | 2 +- src/Events/Webhook/BoostActivatedEvent.php | 10 +- src/Events/Webhook/BoostExpiredEvent.php | 10 +- src/Events/Webhook/LimitReachedEvent.php | 8 +- src/Events/Webhook/LimitWarningEvent.php | 8 +- src/Events/Webhook/PackageChangedEvent.php | 8 +- src/Exceptions/EntitlementException.php | 2 +- .../MissingWorkspaceContextException.php | 2 +- src/Features/ApolloTier.php | 10 +- src/Features/BetaFeatures.php | 2 +- src/Features/HadesTier.php | 10 +- src/Features/UnlimitedWorkspaces.php | 10 +- src/Jobs/ComputeUserStats.php | 6 +- src/Jobs/DispatchEntitlementWebhook.php | 6 +- src/Jobs/ProcessAccountDeletion.php | 4 +- src/Listeners/SendWelcomeEmail.php | 4 +- src/Mail/AccountDeletionRequested.php | 4 +- src/Middleware/CheckWorkspacePermission.php | 6 +- src/Middleware/RequireAdminDomain.php | 2 +- src/Middleware/RequireWorkspaceContext.php | 8 +- src/Middleware/ResolveNamespace.php | 4 +- .../ResolveWorkspaceFromSubdomain.php | 6 +- src/Models/AccountDeletionRequest.php | 2 +- src/Models/AgentReferralBonus.php | 2 +- src/Models/Boost.php | 2 +- src/Models/EntitlementLog.php | 2 +- src/Models/EntitlementWebhook.php | 6 +- src/Models/EntitlementWebhookDelivery.php | 4 +- src/Models/Feature.php | 2 +- src/Models/NamespacePackage.php | 2 +- src/Models/Namespace_.php | 2 +- src/Models/Package.php | 2 +- src/Models/UsageAlertHistory.php | 2 +- src/Models/UsageRecord.php | 2 +- src/Models/User.php | 8 +- src/Models/UserToken.php | 6 +- src/Models/UserTwoFactorAuth.php | 2 +- src/Models/WaitlistEntry.php | 6 +- src/Models/Workspace.php | 14 +- src/Models/WorkspaceInvitation.php | 6 +- src/Models/WorkspaceMember.php | 2 +- src/Models/WorkspacePackage.php | 2 +- src/Models/WorkspaceTeam.php | 4 +- .../BoostExpiredNotification.php | 8 +- src/Notifications/UsageAlertNotification.php | 8 +- .../WaitlistInviteNotification.php | 4 +- src/Notifications/WelcomeNotification.php | 2 +- .../WorkspaceInvitationNotification.php | 4 +- src/Routes/api.php | 2 +- src/Routes/web.php | 8 +- src/Rules/CheckUserPasswordRule.php | 4 +- src/Rules/ResourceStatusRule.php | 2 +- src/Scopes/WorkspaceScope.php | 6 +- src/Services/EntitlementResult.php | 2 +- src/Services/EntitlementService.php | 22 +- src/Services/EntitlementWebhookService.php | 24 +- src/Services/NamespaceManager.php | 8 +- src/Services/NamespaceService.php | 8 +- src/Services/TotpService.php | 4 +- src/Services/UsageAlertService.php | 16 +- src/Services/UserStatsService.php | 8 +- src/Services/WorkspaceCacheManager.php | 4 +- src/Services/WorkspaceManager.php | 6 +- src/Services/WorkspaceService.php | 4 +- src/Services/WorkspaceTeamService.php | 10 +- src/View/Blade/emails/usage-alert.blade.php | 4 +- .../Modal/Admin/EntitlementWebhookManager.php | 10 +- src/View/Modal/Admin/WorkspaceDetails.php | 46 +- src/View/Modal/Admin/WorkspaceManager.php | 6 +- src/View/Modal/Web/CancelDeletion.php | 4 +- src/View/Modal/Web/ConfirmDeletion.php | 4 +- src/View/Modal/Web/WorkspaceHome.php | 6 +- tests/Feature/AccountDeletionTest.php | 8 +- tests/Feature/AuthenticationTest.php | 4 +- tests/Feature/EntitlementApiTest.php | 10 +- tests/Feature/EntitlementServiceTest.php | 20 +- tests/Feature/Guards/AccessTokenGuardTest.php | 4 +- tests/Feature/ProfileTest.php | 4 +- tests/Feature/ResetBillingCyclesTest.php | 18 +- tests/Feature/SettingsTest.php | 4 +- .../Feature/TwoFactorAuthenticatableTest.php | 4 +- tests/Feature/UsageAlertServiceTest.php | 18 +- tests/Feature/WaitlistTest.php | 4 +- tests/Feature/WorkspaceCacheTest.php | 14 +- tests/Feature/WorkspaceInvitationTest.php | 10 +- tests/Feature/WorkspaceSecurityTest.php | 14 +- tests/Feature/WorkspaceTenancyTest.php | 6 +- 126 files changed, 1848 insertions(+), 627 deletions(-) delete mode 100644 .github/package-workflows/README.md delete mode 100644 .github/package-workflows/ci.yml delete mode 100644 .github/package-workflows/release.yml create mode 100644 Boot.php create mode 100644 Routes/admin.php create mode 100644 View/Blade/admin/member-manager.blade.php create mode 100644 View/Blade/admin/team-manager.blade.php create mode 100644 View/Modal/Admin/MemberManager.php create mode 100644 View/Modal/Admin/TeamManager.php delete mode 100644 phpunit.xml diff --git a/.github/package-workflows/README.md b/.github/package-workflows/README.md deleted file mode 100644 index 999966f..0000000 --- a/.github/package-workflows/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Package Workflows - -These workflow templates are for **library packages** (host-uk/core, host-uk/core-api, etc.), not application projects. - -## README Badges - -Add these badges to your package README (replace `{package}` with your package name): - -```markdown -[![CI](https://github.com/host-uk/{package}/actions/workflows/ci.yml/badge.svg)](https://github.com/host-uk/{package}/actions/workflows/ci.yml) -[![codecov](https://codecov.io/gh/host-uk/{package}/graph/badge.svg)](https://codecov.io/gh/host-uk/{package}) -[![Latest Version](https://img.shields.io/packagist/v/host-uk/{package})](https://packagist.org/packages/host-uk/{package}) -[![PHP Version](https://img.shields.io/packagist/php-v/host-uk/{package})](https://packagist.org/packages/host-uk/{package}) -[![License](https://img.shields.io/badge/License-EUPL--1.2-blue.svg)](LICENSE) -``` - -## Usage - -Copy the relevant workflows to your library's `.github/workflows/` directory: - -```bash -# In your library repo -mkdir -p .github/workflows -cp path/to/core-template/.github/package-workflows/ci.yml .github/workflows/ -cp path/to/core-template/.github/package-workflows/release.yml .github/workflows/ -``` - -## Workflows - -### ci.yml -- Runs on push/PR to main -- Tests against PHP 8.2, 8.3, 8.4 -- Tests against Laravel 11 and 12 -- Runs Pint linting -- Runs Pest tests - -### release.yml -- Triggers on version tags (v*) -- Generates changelog using git-cliff -- Creates GitHub release - -## Requirements - -For these workflows to work, your package needs: - -1. **cliff.toml** - Copy from core-template root -2. **Pest configured** - `composer require pestphp/pest --dev` -3. **Pint configured** - `composer require laravel/pint --dev` -4. **CODECOV_TOKEN** - Add to repo secrets for coverage uploads -5. **FUNDING.yml** - Copy `.github/FUNDING.yml` for sponsor button - -## Recommended composer.json scripts - -```json -{ - "scripts": { - "lint": "pint", - "test": "pest", - "test:coverage": "pest --coverage" - } -} -``` diff --git a/.github/package-workflows/ci.yml b/.github/package-workflows/ci.yml deleted file mode 100644 index 7c5f722..0000000 --- a/.github/package-workflows/ci.yml +++ /dev/null @@ -1,55 +0,0 @@ -# CI workflow for library packages (host-uk/core-*, etc.) -# Copy this to .github/workflows/ci.yml in library repos - -name: CI - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - tests: - runs-on: ubuntu-latest - - strategy: - fail-fast: true - matrix: - php: [8.2, 8.3, 8.4] - laravel: [11.*, 12.*] - exclude: - - php: 8.2 - laravel: 12.* - - name: PHP ${{ matrix.php }} / Laravel ${{ matrix.laravel }} - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite - coverage: pcov - - - name: Install dependencies - run: | - composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update - composer update --prefer-dist --no-interaction --no-progress - - - name: Run Pint - run: vendor/bin/pint --test - - - name: Run tests - run: vendor/bin/pest --ci --coverage --coverage-clover coverage.xml - - - name: Upload coverage to Codecov - if: matrix.php == '8.3' && matrix.laravel == '12.*' - uses: codecov/codecov-action@v4 - with: - files: coverage.xml - fail_ci_if_error: false - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/package-workflows/release.yml b/.github/package-workflows/release.yml deleted file mode 100644 index 035294e..0000000 --- a/.github/package-workflows/release.yml +++ /dev/null @@ -1,40 +0,0 @@ -# Release workflow for library packages -# Copy this to .github/workflows/release.yml in library repos - -name: Release - -on: - push: - tags: - - 'v*' - -permissions: - contents: write - -jobs: - release: - runs-on: ubuntu-latest - name: Create Release - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Generate changelog - id: changelog - uses: orhun/git-cliff-action@v3 - with: - config: cliff.toml - args: --latest --strip header - env: - OUTPUT: CHANGELOG.md - - - name: Create release - uses: softprops/action-gh-release@v2 - with: - body_path: CHANGELOG.md - generate_release_notes: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Boot.php b/Boot.php new file mode 100644 index 0000000..40eb94d --- /dev/null +++ b/Boot.php @@ -0,0 +1,56 @@ + + */ + public static array $listens = [ + AdminPanelBooting::class => 'onAdminPanel', + ]; + + public function register(): void + { + // + } + + public function boot(): void + { + // + } + + // ------------------------------------------------------------------------- + // Event-driven handlers + // ------------------------------------------------------------------------- + + public function onAdminPanel(AdminPanelBooting $event): void + { + $event->views($this->moduleName, __DIR__.'/View/Blade'); + + if (file_exists(__DIR__.'/Routes/admin.php')) { + $event->routes(fn () => require __DIR__.'/Routes/admin.php'); + } + + // Admin components + $event->livewire('tenant.admin.team-manager', View\Modal\Admin\TeamManager::class); + $event->livewire('tenant.admin.member-manager', View\Modal\Admin\MemberManager::class); + } +} diff --git a/README.md b/README.md index 4785c04..2dfc1cb 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ The module uses the Core PHP configuration system. Key settings can be configure ## Documentation - [Core PHP Framework](https://github.com/host-uk/core-php) -- [Getting Started Guide](https://host-uk.github.io/core-php/guide/) +- [Getting Started Guide](https://core.help/guide/) ## License diff --git a/Routes/admin.php b/Routes/admin.php new file mode 100644 index 0000000..4fc34d8 --- /dev/null +++ b/Routes/admin.php @@ -0,0 +1,24 @@ +prefix('admin/tenant')->name('hub.admin.tenant.')->group(function () { + // Team Manager + Route::get('/teams', \Core\Tenant\View\Modal\Admin\TeamManager::class) + ->name('teams'); + + // Member Manager + Route::get('/members', \Core\Tenant\View\Modal\Admin\MemberManager::class) + ->name('members'); +}); diff --git a/View/Blade/admin/member-manager.blade.php b/View/Blade/admin/member-manager.blade.php new file mode 100644 index 0000000..ab53310 --- /dev/null +++ b/View/Blade/admin/member-manager.blade.php @@ -0,0 +1,370 @@ + + + + {{-- Stats cards --}} +
+
+
+
+ +
+
+
{{ number_format($this->stats['total_members']) }}
+
{{ __('tenant::tenant.admin.member_manager.stats.total_members') }}
+
+
+
+
+
+
+ +
+
+
{{ number_format($this->stats['with_team']) }}
+
{{ __('tenant::tenant.admin.member_manager.stats.with_team') }}
+
+
+
+
+
+
+ +
+
+
{{ number_format($this->stats['with_custom_permissions']) }}
+
{{ __('tenant::tenant.admin.member_manager.stats.with_custom') }}
+
+
+
+
+ + + + + + + + {{-- Bulk action bar --}} + @if(count($selected) > 0) +
+
+ + + {{ __('tenant::tenant.admin.member_manager.bulk.selected', ['count' => count($selected)]) }} + +
+
+ @if($workspaceFilter) + + {{ __('tenant::tenant.admin.member_manager.bulk.assign_team') }} + + @endif + + {{ __('tenant::tenant.admin.member_manager.bulk.remove_team') }} + + + {{ __('tenant::tenant.admin.member_manager.bulk.clear_permissions') }} + + + {{ __('tenant::tenant.admin.member_manager.bulk.clear') }} + +
+
+ @endif + + {{-- Members table --}} + @if($this->members->isEmpty()) +
+
+
+ +
+ {{ __('tenant::tenant.admin.member_manager.empty_state.title') }} + {{ __('tenant::tenant.admin.member_manager.empty_state.description') }} +
+
+ @else +
+
+ + + + + + + + + + + + + + @foreach($this->members as $member) + + {{-- Checkbox --}} + + + {{-- Member info --}} + + + {{-- Workspace --}} + + + {{-- Team --}} + + + {{-- Legacy role --}} + + + {{-- Custom permissions indicator --}} + + + {{-- Actions --}} + + + @endforeach + +
+ + {{ __('tenant::tenant.admin.member_manager.columns.member') }}{{ __('tenant::tenant.admin.member_manager.columns.workspace') }}{{ __('tenant::tenant.admin.member_manager.columns.team') }}{{ __('tenant::tenant.admin.member_manager.columns.role') }}{{ __('tenant::tenant.admin.member_manager.columns.permissions') }}{{ __('tenant::tenant.admin.member_manager.columns.actions') }}
+ + +
+ @if($member->user?->avatar_url) + + @else +
+ +
+ @endif +
+
{{ $member->user?->name ?? __('tenant::tenant.common.unknown') }}
+
{{ $member->user?->email }}
+
+
+
+
{{ $member->workspace?->name ?? __('tenant::tenant.common.na') }}
+
+ @if($member->team) + + {{ $member->team->name }} + + @else + {{ __('tenant::tenant.admin.member_manager.labels.no_team') }} + @endif + + {{ $member->role }} + + @php + $customPerms = $member->custom_permissions ?? []; + $grantCount = count(array_filter($customPerms, fn($p) => !str_starts_with($p, '-'))); + $revokeCount = count(array_filter($customPerms, fn($p) => str_starts_with($p, '-'))); + @endphp + @if(!empty($customPerms)) +
+ @if($grantCount > 0) + +{{ $grantCount }} + @endif + @if($revokeCount > 0) + -{{ $revokeCount }} + @endif +
+ @else + {{ __('tenant::tenant.admin.member_manager.labels.inherited') }} + @endif +
+ + + + + {{ __('tenant::tenant.admin.member_manager.actions.assign_team') }} + + + {{ __('tenant::tenant.admin.member_manager.actions.custom_permissions') }} + + @if($member->team_id) + + {{ __('tenant::tenant.admin.member_manager.actions.remove_from_team') }} + + @endif + @if(!empty($member->custom_permissions)) + + + {{ __('tenant::tenant.admin.member_manager.actions.clear_permissions') }} + + @endif + + +
+
+ + @if($this->members->hasPages()) +
+ {{ $this->members->links() }} +
+ @endif +
+ @endif + + {{-- Assign to Team Modal --}} + + + {{ __('tenant::tenant.admin.member_manager.assign_modal.title') }} + + +
+ + + @foreach($this->teamsForAssignment as $team) + + @endforeach + + +
+ + {{ __('tenant::tenant.admin.member_manager.modal.actions.cancel') }} + + + {{ __('tenant::tenant.admin.member_manager.modal.actions.save') }} + +
+
+
+ + {{-- Custom Permissions Modal --}} + + + {{ __('tenant::tenant.admin.member_manager.permissions_modal.title') }} + + + @if($this->memberForPermissions) +
+ {{-- Member info --}} +
+ @if($this->memberForPermissions->user?->avatar_url) + + @else +
+ +
+ @endif +
+
{{ $this->memberForPermissions->user?->name }}
+
+ {{ __('tenant::tenant.admin.member_manager.permissions_modal.team_permissions', ['team' => $this->memberForPermissions->team?->name ?? __('tenant::tenant.common.none')]) }} +
+
+
+ +
+ + {{ __('tenant::tenant.admin.member_manager.permissions_modal.description') }} +
+ +
+ {{-- Granted permissions (additions) --}} +
+ + +
+ @foreach($this->permissionGroups as $groupKey => $group) +
+
{{ $group['label'] }}
+
+ @foreach($group['permissions'] as $permKey => $permLabel) + + @endforeach +
+
+ @endforeach +
+
+ + {{-- Revoked permissions (removals) --}} +
+ + +
+ @foreach($this->permissionGroups as $groupKey => $group) +
+
{{ $group['label'] }}
+
+ @foreach($group['permissions'] as $permKey => $permLabel) + + @endforeach +
+
+ @endforeach +
+
+ +
+ + {{ __('tenant::tenant.admin.member_manager.modal.actions.cancel') }} + + + {{ __('tenant::tenant.admin.member_manager.modal.actions.save') }} + +
+
+
+ @endif +
+ + {{-- Bulk Assign Modal --}} + + + {{ __('tenant::tenant.admin.member_manager.bulk_assign_modal.title') }} + + +
+
+ {{ __('tenant::tenant.admin.member_manager.bulk_assign_modal.description', ['count' => count($selected)]) }} +
+ + + + @foreach($this->teamsForBulkAssignment as $team) + + @endforeach + + +
+ + {{ __('tenant::tenant.admin.member_manager.modal.actions.cancel') }} + + + {{ __('tenant::tenant.admin.member_manager.modal.actions.assign') }} + +
+
+
+
diff --git a/View/Blade/admin/team-manager.blade.php b/View/Blade/admin/team-manager.blade.php new file mode 100644 index 0000000..ca70046 --- /dev/null +++ b/View/Blade/admin/team-manager.blade.php @@ -0,0 +1,276 @@ + + + + +
+ @if($workspaceFilter) + + + + + {{ __('tenant::tenant.admin.team_manager.actions.seed_defaults') }} + + + {{ __('tenant::tenant.admin.team_manager.actions.migrate_members') }} + + + + @endif + + {{ __('tenant::tenant.admin.team_manager.actions.create_team') }} + +
+
+ + {{-- Stats cards --}} +
+
+
+
+ +
+
+
{{ number_format($this->stats['total_teams']) }}
+
{{ __('tenant::tenant.admin.team_manager.stats.total_teams') }}
+
+
+
+
+
+
+ +
+
+
{{ number_format($this->stats['total_members']) }}
+
{{ __('tenant::tenant.admin.team_manager.stats.total_members') }}
+
+
+
+
+
+
+ +
+
+
{{ number_format($this->stats['members_with_team']) }}
+
{{ __('tenant::tenant.admin.team_manager.stats.members_assigned') }}
+
+
+
+
+ + + + + + + {{-- Teams table --}} + @if($this->teams->isEmpty()) +
+
+
+ +
+ {{ __('tenant::tenant.admin.team_manager.empty_state.title') }} + {{ __('tenant::tenant.admin.team_manager.empty_state.description') }} +
+ + {{ __('tenant::tenant.admin.team_manager.actions.create_team') }} + + @if($workspaceFilter) + + {{ __('tenant::tenant.admin.team_manager.actions.seed_defaults') }} + + @endif +
+
+
+ @else +
+
+ + + + + + + + + + + + @foreach($this->teams as $team) + + {{-- Team info --}} + + + {{-- Workspace --}} + + + {{-- Member count --}} + + + {{-- Permissions count --}} + + + {{-- Actions --}} + + + @endforeach + +
{{ __('tenant::tenant.admin.team_manager.columns.team') }}{{ __('tenant::tenant.admin.team_manager.columns.workspace') }}{{ __('tenant::tenant.admin.team_manager.columns.members') }}{{ __('tenant::tenant.admin.team_manager.columns.permissions') }}{{ __('tenant::tenant.admin.team_manager.columns.actions') }}
+
+
+ +
+
+
+ {{ $team->name }} + @if($team->is_system) + {{ __('tenant::tenant.admin.team_manager.badges.system') }} + @endif + @if($team->is_default) + {{ __('tenant::tenant.admin.team_manager.badges.default') }} + @endif +
+ @if($team->description) +
{{ $team->description }}
+ @endif +
+
+
+
{{ $team->workspace?->name ?? __('tenant::tenant.common.na') }}
+
+ {{ number_format($team->members_count) }} + +
+ {{ count($team->permissions ?? []) }} {{ __('tenant::tenant.admin.team_manager.labels.permissions') }} +
+
+ + + + + {{ __('tenant::tenant.admin.team_manager.actions.edit') }} + + + {{ __('tenant::tenant.admin.team_manager.actions.view_members') }} + + @unless($team->is_system) + + + {{ __('tenant::tenant.admin.team_manager.actions.delete') }} + + @endunless + + +
+
+ + @if($this->teams->hasPages()) +
+ {{ $this->teams->links() }} +
+ @endif +
+ @endif + + {{-- Create/Edit Team Modal --}} + + + {{ $editingTeamId ? __('tenant::tenant.admin.team_manager.modal.title_edit') : __('tenant::tenant.admin.team_manager.modal.title_create') }} + + +
+ + + @foreach($this->workspaces as $workspace) + + @endforeach + + +
+ + + @unless($editingTeamId) + + @endunless +
+ + + +
+ + @foreach($this->colourOptions as $value => $label) + + @endforeach + + +
+ +
+
+ + {{-- Permissions matrix --}} +
+ + +
+ @foreach($this->permissionGroups as $groupKey => $group) +
+
{{ $group['label'] }}
+
+ @foreach($group['permissions'] as $permKey => $permLabel) + + @endforeach +
+
+ @endforeach +
+
+ +
+ + {{ __('tenant::tenant.admin.team_manager.modal.actions.cancel') }} + + + {{ $editingTeamId ? __('tenant::tenant.admin.team_manager.modal.actions.update') : __('tenant::tenant.admin.team_manager.modal.actions.create') }} + +
+ +
+
diff --git a/View/Modal/Admin/MemberManager.php b/View/Modal/Admin/MemberManager.php new file mode 100644 index 0000000..9d30db3 --- /dev/null +++ b/View/Modal/Admin/MemberManager.php @@ -0,0 +1,404 @@ +checkHadesAccess(); + $this->workspaceFilter = $workspaceFilter; + $this->teamFilter = $teamFilter; + } + + public function updatingSearch(): void + { + $this->resetPage(); + $this->clearSelection(); + } + + public function updatingWorkspaceFilter(): void + { + $this->resetPage(); + $this->clearSelection(); + $this->teamFilter = null; + } + + public function updatingTeamFilter(): void + { + $this->resetPage(); + $this->clearSelection(); + } + + public function updatedSelectAll(bool $value): void + { + $this->selected = $value + ? $this->members->pluck('id')->map(fn ($id) => (string) $id)->toArray() + : []; + } + + public function clearSelection(): void + { + $this->selected = []; + $this->selectAll = false; + } + + // ───────────────────────────────────────────────────────────────────────── + // Team Assignment + // ───────────────────────────────────────────────────────────────────────── + + public function openAssignModal(int $memberId): void + { + $member = WorkspaceMember::findOrFail($memberId); + + $this->assignMemberId = $memberId; + $this->assignTeamId = $member->team_id; + $this->showAssignModal = true; + } + + public function saveAssignment(): void + { + $member = WorkspaceMember::findOrFail($this->assignMemberId); + + // Validate team belongs to same workspace + if ($this->assignTeamId) { + $team = WorkspaceTeam::where('id', $this->assignTeamId) + ->where('workspace_id', $member->workspace_id) + ->first(); + + if (! $team) { + session()->flash('error', __('tenant::tenant.admin.member_manager.messages.invalid_team')); + + return; + } + } + + $member->update(['team_id' => $this->assignTeamId]); + + session()->flash('message', __('tenant::tenant.admin.member_manager.messages.team_assigned')); + $this->closeAssignModal(); + } + + public function removeFromTeam(int $memberId): void + { + $member = WorkspaceMember::findOrFail($memberId); + $member->update(['team_id' => null]); + + session()->flash('message', __('tenant::tenant.admin.member_manager.messages.removed_from_team')); + } + + public function closeAssignModal(): void + { + $this->showAssignModal = false; + $this->assignMemberId = null; + $this->assignTeamId = null; + } + + // ───────────────────────────────────────────────────────────────────────── + // Custom Permissions + // ───────────────────────────────────────────────────────────────────────── + + public function openPermissionsModal(int $memberId): void + { + $member = WorkspaceMember::findOrFail($memberId); + + $this->permissionsMemberId = $memberId; + $this->grantedPermissions = []; + $this->revokedPermissions = []; + + // Parse existing custom permissions + foreach ($member->custom_permissions ?? [] as $permission) { + if (str_starts_with($permission, '-')) { + $this->revokedPermissions[] = substr($permission, 1); + } elseif (str_starts_with($permission, '+')) { + $this->grantedPermissions[] = substr($permission, 1); + } else { + $this->grantedPermissions[] = $permission; + } + } + + $this->showPermissionsModal = true; + } + + public function savePermissions(): void + { + $member = WorkspaceMember::findOrFail($this->permissionsMemberId); + + // Build custom permissions array + $customPermissions = []; + + foreach ($this->grantedPermissions as $permission) { + $customPermissions[] = '+'.$permission; + } + + foreach ($this->revokedPermissions as $permission) { + $customPermissions[] = '-'.$permission; + } + + $member->update([ + 'custom_permissions' => ! empty($customPermissions) ? $customPermissions : null, + ]); + + session()->flash('message', __('tenant::tenant.admin.member_manager.messages.permissions_updated')); + $this->closePermissionsModal(); + } + + public function clearPermissions(int $memberId): void + { + $member = WorkspaceMember::findOrFail($memberId); + $member->update(['custom_permissions' => null]); + + session()->flash('message', __('tenant::tenant.admin.member_manager.messages.permissions_cleared')); + } + + public function closePermissionsModal(): void + { + $this->showPermissionsModal = false; + $this->permissionsMemberId = null; + $this->grantedPermissions = []; + $this->revokedPermissions = []; + } + + // ───────────────────────────────────────────────────────────────────────── + // Bulk Operations + // ───────────────────────────────────────────────────────────────────────── + + public function openBulkAssignModal(): void + { + $this->bulkTeamId = null; + $this->showBulkAssignModal = true; + } + + public function closeBulkAssignModal(): void + { + $this->showBulkAssignModal = false; + $this->bulkTeamId = null; + } + + public function bulkAssignTeam(): void + { + if (empty($this->selected)) { + session()->flash('error', __('tenant::tenant.admin.member_manager.messages.no_members_selected')); + + return; + } + + // Validate team exists + if ($this->bulkTeamId) { + $team = WorkspaceTeam::find($this->bulkTeamId); + if (! $team) { + session()->flash('error', __('tenant::tenant.admin.member_manager.messages.invalid_team')); + + return; + } + + // Update only members from same workspace as the team + $updated = WorkspaceMember::whereIn('id', $this->selected) + ->where('workspace_id', $team->workspace_id) + ->update(['team_id' => $this->bulkTeamId]); + } else { + // Remove from teams + $updated = WorkspaceMember::whereIn('id', $this->selected) + ->update(['team_id' => null]); + } + + session()->flash('message', __('tenant::tenant.admin.member_manager.messages.bulk_team_assigned', ['count' => $updated])); + $this->closeBulkAssignModal(); + $this->clearSelection(); + } + + public function bulkRemoveFromTeam(): void + { + if (empty($this->selected)) { + session()->flash('error', __('tenant::tenant.admin.member_manager.messages.no_members_selected')); + + return; + } + + $updated = WorkspaceMember::whereIn('id', $this->selected) + ->update(['team_id' => null]); + + session()->flash('message', __('tenant::tenant.admin.member_manager.messages.bulk_removed_from_team', ['count' => $updated])); + $this->clearSelection(); + } + + public function bulkClearPermissions(): void + { + if (empty($this->selected)) { + session()->flash('error', __('tenant::tenant.admin.member_manager.messages.no_members_selected')); + + return; + } + + $updated = WorkspaceMember::whereIn('id', $this->selected) + ->update(['custom_permissions' => null]); + + session()->flash('message', __('tenant::tenant.admin.member_manager.messages.bulk_permissions_cleared', ['count' => $updated])); + $this->clearSelection(); + } + + // ───────────────────────────────────────────────────────────────────────── + // Computed Properties + // ───────────────────────────────────────────────────────────────────────── + + #[Computed] + public function members(): \Illuminate\Contracts\Pagination\LengthAwarePaginator + { + return WorkspaceMember::query() + ->with(['user', 'workspace', 'team', 'inviter']) + ->when($this->search, function ($query) { + $query->whereHas('user', function ($q) { + $q->where('name', 'like', "%{$this->search}%") + ->orWhere('email', 'like', "%{$this->search}%"); + }); + }) + ->when($this->workspaceFilter, function ($query) { + $query->where('workspace_id', $this->workspaceFilter); + }) + ->when($this->teamFilter, function ($query) { + $query->where('team_id', $this->teamFilter); + }) + ->orderBy('created_at', 'desc') + ->paginate(20); + } + + #[Computed] + public function workspaces(): \Illuminate\Database\Eloquent\Collection + { + return Workspace::orderBy('name')->get(); + } + + #[Computed] + public function teamsForFilter(): \Illuminate\Database\Eloquent\Collection + { + $query = WorkspaceTeam::query(); + + if ($this->workspaceFilter) { + $query->where('workspace_id', $this->workspaceFilter); + } + + return $query->ordered()->get(); + } + + #[Computed] + public function teamsForAssignment(): \Illuminate\Database\Eloquent\Collection + { + if ($this->assignMemberId) { + $member = WorkspaceMember::find($this->assignMemberId); + if ($member) { + return WorkspaceTeam::where('workspace_id', $member->workspace_id) + ->ordered() + ->get(); + } + } + + return new \Illuminate\Database\Eloquent\Collection; + } + + #[Computed] + public function teamsForBulkAssignment(): \Illuminate\Database\Eloquent\Collection + { + // Only show teams from the current workspace filter + if ($this->workspaceFilter) { + return WorkspaceTeam::where('workspace_id', $this->workspaceFilter) + ->ordered() + ->get(); + } + + return new \Illuminate\Database\Eloquent\Collection; + } + + #[Computed] + public function permissionGroups(): array + { + return WorkspaceTeam::getAvailablePermissions(); + } + + #[Computed] + public function memberForPermissions(): ?WorkspaceMember + { + if ($this->permissionsMemberId) { + return WorkspaceMember::with(['team'])->find($this->permissionsMemberId); + } + + return null; + } + + #[Computed] + public function stats(): array + { + $query = WorkspaceMember::query(); + + if ($this->workspaceFilter) { + $query->where('workspace_id', $this->workspaceFilter); + } + + if ($this->teamFilter) { + $query->where('team_id', $this->teamFilter); + } + + return [ + 'total_members' => (clone $query)->count(), + 'with_team' => (clone $query)->whereNotNull('team_id')->count(), + 'with_custom_permissions' => (clone $query)->whereNotNull('custom_permissions')->count(), + ]; + } + + private function checkHadesAccess(): void + { + if (! auth()->user()?->isHades()) { + abort(403, __('tenant::tenant.errors.hades_required')); + } + } + + public function render(): View + { + return view('tenant::admin.member-manager') + ->layout('hub::admin.layouts.app', ['title' => __('tenant::tenant.admin.member_manager.title')]); + } +} diff --git a/View/Modal/Admin/TeamManager.php b/View/Modal/Admin/TeamManager.php new file mode 100644 index 0000000..ad0f2d6 --- /dev/null +++ b/View/Modal/Admin/TeamManager.php @@ -0,0 +1,289 @@ +checkHadesAccess(); + } + + public function updatingSearch(): void + { + $this->resetPage(); + $this->clearSelection(); + } + + public function updatingWorkspaceFilter(): void + { + $this->resetPage(); + $this->clearSelection(); + } + + public function updatedSelectAll(bool $value): void + { + $this->selected = $value + ? $this->teams->pluck('id')->map(fn ($id) => (string) $id)->toArray() + : []; + } + + public function clearSelection(): void + { + $this->selected = []; + $this->selectAll = false; + } + + // ───────────────────────────────────────────────────────────────────────── + // Team CRUD + // ───────────────────────────────────────────────────────────────────────── + + public function openCreateTeam(): void + { + $this->resetTeamForm(); + $this->showTeamModal = true; + } + + public function openEditTeam(int $id): void + { + $team = WorkspaceTeam::findOrFail($id); + + $this->editingTeamId = $id; + $this->teamName = $team->name; + $this->teamSlug = $team->slug ?? ''; + $this->teamDescription = $team->description ?? ''; + $this->teamPermissions = $team->permissions ?? []; + $this->teamIsDefault = $team->is_default; + $this->teamColour = $team->colour ?? 'zinc'; + $this->teamWorkspaceId = $team->workspace_id; + + $this->showTeamModal = true; + } + + public function saveTeam(): void + { + $this->validate([ + 'teamName' => ['required', 'string', 'max:255'], + 'teamSlug' => ['nullable', 'string', 'max:255', 'alpha_dash'], + 'teamDescription' => ['nullable', 'string', 'max:1000'], + 'teamPermissions' => ['array'], + 'teamIsDefault' => ['boolean'], + 'teamColour' => ['required', 'string', 'max:32'], + 'teamWorkspaceId' => ['required', 'exists:workspaces,id'], + ]); + + $data = [ + 'name' => $this->teamName, + 'description' => $this->teamDescription ?: null, + 'permissions' => $this->teamPermissions, + 'is_default' => $this->teamIsDefault, + 'colour' => $this->teamColour, + 'workspace_id' => $this->teamWorkspaceId, + ]; + + // Only set slug for new teams or if explicitly provided + if (! $this->editingTeamId && $this->teamSlug) { + $data['slug'] = $this->teamSlug; + } + + // If setting as default, unset other defaults for this workspace + if ($this->teamIsDefault) { + WorkspaceTeam::where('workspace_id', $this->teamWorkspaceId) + ->where('is_default', true) + ->when($this->editingTeamId, fn ($q) => $q->where('id', '!=', $this->editingTeamId)) + ->update(['is_default' => false]); + } + + if ($this->editingTeamId) { + $team = WorkspaceTeam::findOrFail($this->editingTeamId); + + // Don't allow editing system team slug + if ($team->is_system) { + unset($data['slug']); + } + + $team->update($data); + session()->flash('message', __('tenant::tenant.admin.team_manager.messages.team_updated')); + } else { + WorkspaceTeam::create($data); + session()->flash('message', __('tenant::tenant.admin.team_manager.messages.team_created')); + } + + $this->closeTeamModal(); + } + + public function deleteTeam(int $id): void + { + $team = WorkspaceTeam::findOrFail($id); + + if ($team->is_system) { + session()->flash('error', __('tenant::tenant.admin.team_manager.messages.cannot_delete_system')); + + return; + } + + $memberCount = WorkspaceMember::where('team_id', $team->id)->count(); + if ($memberCount > 0) { + session()->flash('error', __('tenant::tenant.admin.team_manager.messages.cannot_delete_has_members', ['count' => $memberCount])); + + return; + } + + $team->delete(); + session()->flash('message', __('tenant::tenant.admin.team_manager.messages.team_deleted')); + } + + public function closeTeamModal(): void + { + $this->showTeamModal = false; + $this->resetTeamForm(); + } + + protected function resetTeamForm(): void + { + $this->editingTeamId = null; + $this->teamName = ''; + $this->teamSlug = ''; + $this->teamDescription = ''; + $this->teamPermissions = []; + $this->teamIsDefault = false; + $this->teamColour = 'zinc'; + $this->teamWorkspaceId = null; + } + + public function seedDefaultTeams(int $workspaceId): void + { + $workspace = Workspace::findOrFail($workspaceId); + + $teamService = app(WorkspaceTeamService::class)->forWorkspace($workspace); + $teamService->seedDefaultTeams(); + + session()->flash('message', __('tenant::tenant.admin.team_manager.messages.defaults_seeded')); + } + + public function migrateMembers(int $workspaceId): void + { + $workspace = Workspace::findOrFail($workspaceId); + + $teamService = app(WorkspaceTeamService::class)->forWorkspace($workspace); + $migrated = $teamService->migrateExistingMembers(); + + session()->flash('message', __('tenant::tenant.admin.team_manager.messages.members_migrated', ['count' => $migrated])); + } + + // ───────────────────────────────────────────────────────────────────────── + // Computed Properties + // ───────────────────────────────────────────────────────────────────────── + + #[Computed] + public function teams(): \Illuminate\Contracts\Pagination\LengthAwarePaginator + { + return WorkspaceTeam::query() + ->with(['workspace']) + ->when($this->search, function ($query) { + $query->where(function ($q) { + $q->where('name', 'like', "%{$this->search}%") + ->orWhere('description', 'like', "%{$this->search}%"); + }); + }) + ->when($this->workspaceFilter, function ($query) { + $query->where('workspace_id', $this->workspaceFilter); + }) + ->withCount('members') + ->orderBy('workspace_id') + ->ordered() + ->paginate(20); + } + + #[Computed] + public function workspaces(): \Illuminate\Database\Eloquent\Collection + { + return Workspace::orderBy('name')->get(); + } + + #[Computed] + public function permissionGroups(): array + { + return WorkspaceTeam::getAvailablePermissions(); + } + + #[Computed] + public function colourOptions(): array + { + return WorkspaceTeam::getColourOptions(); + } + + #[Computed] + public function stats(): array + { + $teamQuery = WorkspaceTeam::query(); + $memberQuery = WorkspaceMember::query(); + + if ($this->workspaceFilter) { + $teamQuery->where('workspace_id', $this->workspaceFilter); + $memberQuery->where('workspace_id', $this->workspaceFilter); + } + + return [ + 'total_teams' => $teamQuery->count(), + 'total_members' => $memberQuery->count(), + 'members_with_team' => (clone $memberQuery)->whereNotNull('team_id')->count(), + ]; + } + + private function checkHadesAccess(): void + { + if (! auth()->user()?->isHades()) { + abort(403, __('tenant::tenant.errors.hades_required')); + } + } + + public function render(): View + { + return view('tenant::admin.team-manager') + ->layout('hub::admin.layouts.app', ['title' => __('tenant::tenant.admin.team_manager.title')]); + } +} diff --git a/composer.json b/composer.json index 478fdba..2c0bfea 100644 --- a/composer.json +++ b/composer.json @@ -1,56 +1,48 @@ { "name": "host-uk/core-tenant", - "description": "Multi-tenancy module for Core PHP Framework - users, workspaces, entitlements", - "keywords": ["laravel", "core-php", "tenancy", "multi-tenant", "workspace", "entitlements"], - "license": "EUPL-1.2", - "authors": [ - { - "name": "Host UK", - "email": "support@host.uk.com" - } + "description": "Multi-tenancy and workspaces for Laravel", + "keywords": [ + "multi-tenant", + "workspaces", + "teams" ], + "license": "EUPL-1.2", "require": { "php": "^8.2", - "laravel/framework": "^11.0|^12.0", - "host-uk/core": "^1.0|dev-main" + "host-uk/core": "dev-main" }, "require-dev": { - "fakerphp/faker": "^1.23", "laravel/pint": "^1.18", - "mockery/mockery": "^1.6", - "nunomaduro/collision": "^8.6", "orchestra/testbench": "^9.0|^10.0", - "phpunit/phpunit": "^11.5" + "pestphp/pest": "^3.0" }, "autoload": { "psr-4": { - "Core\\Mod\\Tenant\\": "src/" + "Core\\Tenant\\": "" } }, "autoload-dev": { "psr-4": { - "Core\\Mod\\Tenant\\Tests\\": "tests/" + "Core\\Tenant\\Tests\\": "Tests/" } }, - "scripts": { - "test": "vendor/bin/phpunit", - "pint": "vendor/bin/pint" - }, "extra": { "laravel": { "providers": [ - "Core\\Mod\\Tenant\\Boot" + "Core\\Tenant\\Boot" ] } }, + "scripts": { + "lint": "pint", + "test": "pest" + }, "config": { - "optimize-autoloader": true, - "preferred-install": "dist", "sort-packages": true, "allow-plugins": { - "php-http/discovery": true + "pestphp/pest-plugin": true } }, - "minimum-stability": "stable", + "minimum-stability": "dev", "prefer-stable": true } diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index 02250d6..0000000 --- a/phpunit.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - tests/Unit - - - tests/Feature - - - - - src - - - - - - - - - - - - - - - - diff --git a/src/Boot.php b/src/Boot.php index 354c780..a1cc06e 100644 --- a/src/Boot.php +++ b/src/Boot.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Core\Mod\Tenant; +namespace Core\Core\Tenant; use Core\Events\AdminPanelBooting; use Core\Events\ApiRoutesRegistering; @@ -40,48 +40,48 @@ class Boot extends ServiceProvider public function register(): void { $this->app->singleton( - \Core\Mod\Tenant\Contracts\TwoFactorAuthenticationProvider::class, - \Core\Mod\Tenant\Services\TotpService::class + \Core\Core\Tenant\Contracts\TwoFactorAuthenticationProvider::class, + \Core\Core\Tenant\Services\TotpService::class ); $this->app->singleton( - \Core\Mod\Tenant\Services\EntitlementService::class, - \Core\Mod\Tenant\Services\EntitlementService::class + \Core\Core\Tenant\Services\EntitlementService::class, + \Core\Core\Tenant\Services\EntitlementService::class ); $this->app->singleton( - \Core\Mod\Tenant\Services\WorkspaceManager::class, - \Core\Mod\Tenant\Services\WorkspaceManager::class + \Core\Core\Tenant\Services\WorkspaceManager::class, + \Core\Core\Tenant\Services\WorkspaceManager::class ); $this->app->singleton( - \Core\Mod\Tenant\Services\UserStatsService::class, - \Core\Mod\Tenant\Services\UserStatsService::class + \Core\Core\Tenant\Services\UserStatsService::class, + \Core\Core\Tenant\Services\UserStatsService::class ); $this->app->singleton( - \Core\Mod\Tenant\Services\WorkspaceService::class, - \Core\Mod\Tenant\Services\WorkspaceService::class + \Core\Core\Tenant\Services\WorkspaceService::class, + \Core\Core\Tenant\Services\WorkspaceService::class ); $this->app->singleton( - \Core\Mod\Tenant\Services\WorkspaceCacheManager::class, - \Core\Mod\Tenant\Services\WorkspaceCacheManager::class + \Core\Core\Tenant\Services\WorkspaceCacheManager::class, + \Core\Core\Tenant\Services\WorkspaceCacheManager::class ); $this->app->singleton( - \Core\Mod\Tenant\Services\UsageAlertService::class, - \Core\Mod\Tenant\Services\UsageAlertService::class + \Core\Core\Tenant\Services\UsageAlertService::class, + \Core\Core\Tenant\Services\UsageAlertService::class ); $this->app->singleton( - \Core\Mod\Tenant\Services\EntitlementWebhookService::class, - \Core\Mod\Tenant\Services\EntitlementWebhookService::class + \Core\Core\Tenant\Services\EntitlementWebhookService::class, + \Core\Core\Tenant\Services\EntitlementWebhookService::class ); $this->app->singleton( - \Core\Mod\Tenant\Services\WorkspaceTeamService::class, - \Core\Mod\Tenant\Services\WorkspaceTeamService::class + \Core\Core\Tenant\Services\WorkspaceTeamService::class, + \Core\Core\Tenant\Services\WorkspaceTeamService::class ); $this->registerBackwardCompatAliases(); @@ -91,28 +91,28 @@ class Boot extends ServiceProvider { if (! class_exists(\App\Services\WorkspaceManager::class)) { class_alias( - \Core\Mod\Tenant\Services\WorkspaceManager::class, + \Core\Core\Tenant\Services\WorkspaceManager::class, \App\Services\WorkspaceManager::class ); } if (! class_exists(\App\Services\UserStatsService::class)) { class_alias( - \Core\Mod\Tenant\Services\UserStatsService::class, + \Core\Core\Tenant\Services\UserStatsService::class, \App\Services\UserStatsService::class ); } if (! class_exists(\App\Services\WorkspaceService::class)) { class_alias( - \Core\Mod\Tenant\Services\WorkspaceService::class, + \Core\Core\Tenant\Services\WorkspaceService::class, \App\Services\WorkspaceService::class ); } if (! class_exists(\App\Services\WorkspaceCacheManager::class)) { class_alias( - \Core\Mod\Tenant\Services\WorkspaceCacheManager::class, + \Core\Core\Tenant\Services\WorkspaceCacheManager::class, \App\Services\WorkspaceCacheManager::class ); } diff --git a/src/Concerns/BelongsToNamespace.php b/src/Concerns/BelongsToNamespace.php index ab25e5b..9979e10 100644 --- a/src/Concerns/BelongsToNamespace.php +++ b/src/Concerns/BelongsToNamespace.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Concerns; +namespace Core\Core\Tenant\Concerns; -use Core\Mod\Tenant\Models\Namespace_; -use Core\Mod\Tenant\Models\User; +use Core\Core\Tenant\Models\Namespace_; +use Core\Core\Tenant\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Collection; diff --git a/src/Concerns/BelongsToWorkspace.php b/src/Concerns/BelongsToWorkspace.php index 61d45ee..ad53826 100644 --- a/src/Concerns/BelongsToWorkspace.php +++ b/src/Concerns/BelongsToWorkspace.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Concerns; +namespace Core\Core\Tenant\Concerns; -use Core\Mod\Tenant\Exceptions\MissingWorkspaceContextException; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Scopes\WorkspaceScope; -use Core\Mod\Tenant\Services\WorkspaceCacheManager; +use Core\Core\Tenant\Exceptions\MissingWorkspaceContextException; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Scopes\WorkspaceScope; +use Core\Core\Tenant\Services\WorkspaceCacheManager; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Collection; diff --git a/src/Concerns/HasWorkspaceCache.php b/src/Concerns/HasWorkspaceCache.php index 5ba50ba..ccce31b 100644 --- a/src/Concerns/HasWorkspaceCache.php +++ b/src/Concerns/HasWorkspaceCache.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Concerns; +namespace Core\Core\Tenant\Concerns; use Closure; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Services\WorkspaceCacheManager; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Services\WorkspaceCacheManager; use Illuminate\Support\Collection; /** diff --git a/src/Concerns/TwoFactorAuthenticatable.php b/src/Concerns/TwoFactorAuthenticatable.php index f838870..acbd1b9 100644 --- a/src/Concerns/TwoFactorAuthenticatable.php +++ b/src/Concerns/TwoFactorAuthenticatable.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Concerns; +namespace Core\Core\Tenant\Concerns; -use Core\Mod\Tenant\Contracts\TwoFactorAuthenticationProvider; -use Core\Mod\Tenant\Models\UserTwoFactorAuth; -use Core\Mod\Tenant\Services\TotpService; +use Core\Core\Tenant\Contracts\TwoFactorAuthenticationProvider; +use Core\Core\Tenant\Models\UserTwoFactorAuth; +use Core\Core\Tenant\Services\TotpService; use Illuminate\Database\Eloquent\Relations\HasOne; /** diff --git a/src/Console/Commands/CheckUsageAlerts.php b/src/Console/Commands/CheckUsageAlerts.php index 35cf2ca..977137e 100644 --- a/src/Console/Commands/CheckUsageAlerts.php +++ b/src/Console/Commands/CheckUsageAlerts.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Console\Commands; +namespace Core\Core\Tenant\Console\Commands; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Services\UsageAlertService; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Services\UsageAlertService; use Illuminate\Console\Command; /** diff --git a/src/Console/Commands/ProcessAccountDeletions.php b/src/Console/Commands/ProcessAccountDeletions.php index 09c57a5..e86ad3f 100644 --- a/src/Console/Commands/ProcessAccountDeletions.php +++ b/src/Console/Commands/ProcessAccountDeletions.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Console\Commands; +namespace Core\Core\Tenant\Console\Commands; -use Core\Mod\Tenant\Models\AccountDeletionRequest; +use Core\Core\Tenant\Models\AccountDeletionRequest; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; diff --git a/src/Console/Commands/RefreshUserStats.php b/src/Console/Commands/RefreshUserStats.php index 2e69729..5631281 100644 --- a/src/Console/Commands/RefreshUserStats.php +++ b/src/Console/Commands/RefreshUserStats.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Console\Commands; +namespace Core\Core\Tenant\Console\Commands; -use Core\Mod\Tenant\Jobs\ComputeUserStats; -use Core\Mod\Tenant\Models\User; +use Core\Core\Tenant\Jobs\ComputeUserStats; +use Core\Core\Tenant\Models\User; use Illuminate\Console\Command; class RefreshUserStats extends Command diff --git a/src/Console/Commands/ResetBillingCycles.php b/src/Console/Commands/ResetBillingCycles.php index 4c64106..42ff284 100644 --- a/src/Console/Commands/ResetBillingCycles.php +++ b/src/Console/Commands/ResetBillingCycles.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Console\Commands; +namespace Core\Core\Tenant\Console\Commands; -use Core\Mod\Tenant\Models\Boost; -use Core\Mod\Tenant\Models\EntitlementLog; -use Core\Mod\Tenant\Models\UsageRecord; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Notifications\BoostExpiredNotification; -use Core\Mod\Tenant\Services\EntitlementService; +use Core\Core\Tenant\Models\Boost; +use Core\Core\Tenant\Models\EntitlementLog; +use Core\Core\Tenant\Models\UsageRecord; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Notifications\BoostExpiredNotification; +use Core\Core\Tenant\Services\EntitlementService; use Illuminate\Console\Command; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; diff --git a/src/Contracts/EntitlementWebhookEvent.php b/src/Contracts/EntitlementWebhookEvent.php index 569a070..eacfed5 100644 --- a/src/Contracts/EntitlementWebhookEvent.php +++ b/src/Contracts/EntitlementWebhookEvent.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Contracts; +namespace Core\Core\Tenant\Contracts; /** * Contract for entitlement webhook events. diff --git a/src/Contracts/TwoFactorAuthenticationProvider.php b/src/Contracts/TwoFactorAuthenticationProvider.php index eb5230b..bf5c768 100644 --- a/src/Contracts/TwoFactorAuthenticationProvider.php +++ b/src/Contracts/TwoFactorAuthenticationProvider.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Contracts; +namespace Core\Core\Tenant\Contracts; /** * Contract for two-factor authentication providers. diff --git a/src/Controllers/Api/EntitlementWebhookController.php b/src/Controllers/Api/EntitlementWebhookController.php index fe3863a..f756e0a 100644 --- a/src/Controllers/Api/EntitlementWebhookController.php +++ b/src/Controllers/Api/EntitlementWebhookController.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Controllers\Api; +namespace Core\Core\Tenant\Controllers\Api; -use Core\Mod\Tenant\Models\EntitlementWebhook; -use Core\Mod\Tenant\Models\EntitlementWebhookDelivery; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Services\EntitlementWebhookService; +use Core\Core\Tenant\Models\EntitlementWebhook; +use Core\Core\Tenant\Models\EntitlementWebhookDelivery; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Services\EntitlementWebhookService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Routing\Controller; diff --git a/src/Controllers/EntitlementApiController.php b/src/Controllers/EntitlementApiController.php index e55edb6..dcb74ee 100644 --- a/src/Controllers/EntitlementApiController.php +++ b/src/Controllers/EntitlementApiController.php @@ -2,19 +2,19 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Controllers; +namespace Core\Core\Tenant\Controllers; use Core\Front\Controller; use Illuminate\Auth\Events\Registered; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Str; -use Mod\Tenant\Models\EntitlementLog; -use Mod\Tenant\Models\Package; -use Mod\Tenant\Models\User; -use Mod\Tenant\Models\Workspace; -use Mod\Tenant\Models\WorkspacePackage; -use Mod\Tenant\Services\EntitlementService; +use Core\Tenant\Models\EntitlementLog; +use Core\Tenant\Models\Package; +use Core\Tenant\Models\User; +use Core\Tenant\Models\Workspace; +use Core\Tenant\Models\WorkspacePackage; +use Core\Tenant\Services\EntitlementService; class EntitlementApiController extends Controller { diff --git a/src/Controllers/ReferralController.php b/src/Controllers/ReferralController.php index 2382ac1..395a10d 100644 --- a/src/Controllers/ReferralController.php +++ b/src/Controllers/ReferralController.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Controllers; +namespace Core\Core\Tenant\Controllers; use Core\Helpers\PrivacyHelper; use Core\Mod\Trees\Models\TreePlanting; diff --git a/src/Controllers/WorkspaceController.php b/src/Controllers/WorkspaceController.php index 91c7d68..b67cbf7 100644 --- a/src/Controllers/WorkspaceController.php +++ b/src/Controllers/WorkspaceController.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Controllers; +namespace Core\Core\Tenant\Controllers; use Core\Front\Controller; use Illuminate\Http\JsonResponse; @@ -11,8 +11,8 @@ use Mod\Api\Controllers\Concerns\HasApiResponses; use Mod\Api\Controllers\Concerns\ResolvesWorkspace; use Mod\Api\Resources\PaginatedCollection; use Mod\Api\Resources\WorkspaceResource; -use Mod\Tenant\Models\User; -use Mod\Tenant\Models\Workspace; +use Core\Tenant\Models\User; +use Core\Tenant\Models\Workspace; /** * Workspace API controller. diff --git a/src/Controllers/WorkspaceInvitationController.php b/src/Controllers/WorkspaceInvitationController.php index 999d1ff..95f03ce 100644 --- a/src/Controllers/WorkspaceInvitationController.php +++ b/src/Controllers/WorkspaceInvitationController.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Controllers; +namespace Core\Core\Tenant\Controllers; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Models\WorkspaceInvitation; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Models\WorkspaceInvitation; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Routing\Controller; diff --git a/src/Database/Factories/UserFactory.php b/src/Database/Factories/UserFactory.php index 3a56e26..8d1ca5f 100644 --- a/src/Database/Factories/UserFactory.php +++ b/src/Database/Factories/UserFactory.php @@ -1,13 +1,13 @@ + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Core\Tenant\Models\User> */ class UserFactory extends Factory { @@ -17,7 +17,7 @@ class UserFactory extends Factory * Uses the backward-compatible alias class to ensure type compatibility * with existing code that expects Mod\Tenant\Models\User. */ - protected $model = \Core\Mod\Tenant\Models\User::class; + protected $model = \Core\Core\Tenant\Models\User::class; /** * The current password being used by the factory. diff --git a/src/Database/Factories/UserTokenFactory.php b/src/Database/Factories/UserTokenFactory.php index dab5b03..ae47149 100644 --- a/src/Database/Factories/UserTokenFactory.php +++ b/src/Database/Factories/UserTokenFactory.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Database\Factories; +namespace Core\Core\Tenant\Database\Factories; -use Core\Mod\Tenant\Models\User; -use Core\Mod\Tenant\Models\UserToken; +use Core\Core\Tenant\Models\User; +use Core\Core\Tenant\Models\UserToken; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; diff --git a/src/Database/Factories/WaitlistEntryFactory.php b/src/Database/Factories/WaitlistEntryFactory.php index 01ca0dd..5696625 100644 --- a/src/Database/Factories/WaitlistEntryFactory.php +++ b/src/Database/Factories/WaitlistEntryFactory.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Database\Factories; +namespace Core\Core\Tenant\Database\Factories; -use Core\Mod\Tenant\Models\WaitlistEntry; +use Core\Core\Tenant\Models\WaitlistEntry; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Mod\Tenant\Models\WaitlistEntry> + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Core\Tenant\Models\WaitlistEntry> */ class WaitlistEntryFactory extends Factory { diff --git a/src/Database/Factories/WorkspaceFactory.php b/src/Database/Factories/WorkspaceFactory.php index 55f4cc2..073f7e7 100644 --- a/src/Database/Factories/WorkspaceFactory.php +++ b/src/Database/Factories/WorkspaceFactory.php @@ -1,12 +1,12 @@ + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Core\Tenant\Models\Workspace> */ class WorkspaceFactory extends Factory { diff --git a/src/Database/Factories/WorkspaceInvitationFactory.php b/src/Database/Factories/WorkspaceInvitationFactory.php index c1771b2..9cc4b9c 100644 --- a/src/Database/Factories/WorkspaceInvitationFactory.php +++ b/src/Database/Factories/WorkspaceInvitationFactory.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Database\Factories; +namespace Core\Core\Tenant\Database\Factories; -use Core\Mod\Tenant\Models\WorkspaceInvitation; +use Core\Core\Tenant\Models\WorkspaceInvitation; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Mod\Tenant\Models\WorkspaceInvitation> + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\Core\Core\Tenant\Models\WorkspaceInvitation> */ class WorkspaceInvitationFactory extends Factory { diff --git a/src/Database/Seeders/DemoTestUserSeeder.php b/src/Database/Seeders/DemoTestUserSeeder.php index d1da763..84fdeb6 100644 --- a/src/Database/Seeders/DemoTestUserSeeder.php +++ b/src/Database/Seeders/DemoTestUserSeeder.php @@ -1,10 +1,10 @@ \Core\Mod\Tenant\Middleware\RequireWorkspaceContext::class, + * 'workspace.required' => \Core\Core\Tenant\Middleware\RequireWorkspaceContext::class, */ class RequireWorkspaceContext { diff --git a/src/Middleware/ResolveNamespace.php b/src/Middleware/ResolveNamespace.php index 9a8eed9..a926837 100644 --- a/src/Middleware/ResolveNamespace.php +++ b/src/Middleware/ResolveNamespace.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Middleware; +namespace Core\Core\Tenant\Middleware; use Closure; -use Core\Mod\Tenant\Services\NamespaceService; +use Core\Core\Tenant\Services\NamespaceService; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/src/Middleware/ResolveWorkspaceFromSubdomain.php b/src/Middleware/ResolveWorkspaceFromSubdomain.php index 9f195ff..3e4a312 100644 --- a/src/Middleware/ResolveWorkspaceFromSubdomain.php +++ b/src/Middleware/ResolveWorkspaceFromSubdomain.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Middleware; +namespace Core\Core\Tenant\Middleware; use Closure; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Services\WorkspaceService; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Services\WorkspaceService; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/src/Models/AccountDeletionRequest.php b/src/Models/AccountDeletionRequest.php index 5716742..49041b3 100644 --- a/src/Models/AccountDeletionRequest.php +++ b/src/Models/AccountDeletionRequest.php @@ -1,6 +1,6 @@ check() && auth()->user() instanceof \Core\Mod\Tenant\Models\User) { + if (auth()->check() && auth()->user() instanceof \Core\Core\Tenant\Models\User) { return auth()->user()->defaultHostWorkspace(); } @@ -680,7 +680,7 @@ class Workspace extends Model ]); // Send notification - $invitation->notify(new \Core\Mod\Tenant\Notifications\WorkspaceInvitationNotification($invitation)); + $invitation->notify(new \Core\Core\Tenant\Notifications\WorkspaceInvitationNotification($invitation)); return $invitation; } diff --git a/src/Models/WorkspaceInvitation.php b/src/Models/WorkspaceInvitation.php index a863a82..be08094 100644 --- a/src/Models/WorkspaceInvitation.php +++ b/src/Models/WorkspaceInvitation.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Models; +namespace Core\Core\Tenant\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -15,9 +15,9 @@ class WorkspaceInvitation extends Model use HasFactory; use Notifiable; - protected static function newFactory(): \Core\Mod\Tenant\Database\Factories\WorkspaceInvitationFactory + protected static function newFactory(): \Core\Core\Tenant\Database\Factories\WorkspaceInvitationFactory { - return \Core\Mod\Tenant\Database\Factories\WorkspaceInvitationFactory::new(); + return \Core\Core\Tenant\Database\Factories\WorkspaceInvitationFactory::new(); } protected $fillable = [ diff --git a/src/Models/WorkspaceMember.php b/src/Models/WorkspaceMember.php index 6d49df7..e345fff 100644 --- a/src/Models/WorkspaceMember.php +++ b/src/Models/WorkspaceMember.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Models; +namespace Core\Core\Tenant\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; diff --git a/src/Models/WorkspacePackage.php b/src/Models/WorkspacePackage.php index 629073b..7f31a21 100644 --- a/src/Models/WorkspacePackage.php +++ b/src/Models/WorkspacePackage.php @@ -1,6 +1,6 @@ name('account.')->group(function () { | */ -Route::get('/workspace/invitation/{token}', \Core\Mod\Tenant\Controllers\WorkspaceInvitationController::class) +Route::get('/workspace/invitation/{token}', \Core\Core\Tenant\Controllers\WorkspaceInvitationController::class) ->name('workspace.invitation.accept'); /* diff --git a/src/Rules/CheckUserPasswordRule.php b/src/Rules/CheckUserPasswordRule.php index 94d5814..56458ec 100644 --- a/src/Rules/CheckUserPasswordRule.php +++ b/src/Rules/CheckUserPasswordRule.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Rules; +namespace Core\Core\Tenant\Rules; use Closure; -use Core\Mod\Tenant\Models\User; +use Core\Core\Tenant\Models\User; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Facades\Hash; diff --git a/src/Rules/ResourceStatusRule.php b/src/Rules/ResourceStatusRule.php index 8d338c0..119965a 100644 --- a/src/Rules/ResourceStatusRule.php +++ b/src/Rules/ResourceStatusRule.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Rules; +namespace Core\Core\Tenant\Rules; use Closure; use Core\Mod\Social\Enums\ResourceStatus; diff --git a/src/Scopes/WorkspaceScope.php b/src/Scopes/WorkspaceScope.php index 3af629f..e066d05 100644 --- a/src/Scopes/WorkspaceScope.php +++ b/src/Scopes/WorkspaceScope.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Scopes; +namespace Core\Core\Tenant\Scopes; -use Core\Mod\Tenant\Exceptions\MissingWorkspaceContextException; -use Core\Mod\Tenant\Models\Workspace; +use Core\Core\Tenant\Exceptions\MissingWorkspaceContextException; +use Core\Core\Tenant\Models\Workspace; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; diff --git a/src/Services/EntitlementResult.php b/src/Services/EntitlementResult.php index 078ba09..e2ec3ab 100644 --- a/src/Services/EntitlementResult.php +++ b/src/Services/EntitlementResult.php @@ -1,6 +1,6 @@ cached_stats) { // Queue background refresh - dispatch(new \Core\Mod\Tenant\Jobs\ComputeUserStats($user->id))->onQueue('stats'); + dispatch(new \Core\Core\Tenant\Jobs\ComputeUserStats($user->id))->onQueue('stats'); return $user->cached_stats; } diff --git a/src/Services/WorkspaceCacheManager.php b/src/Services/WorkspaceCacheManager.php index ef046f8..55f63fc 100644 --- a/src/Services/WorkspaceCacheManager.php +++ b/src/Services/WorkspaceCacheManager.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Services; +namespace Core\Core\Tenant\Services; use Closure; -use Core\Mod\Tenant\Models\Workspace; +use Core\Core\Tenant\Models\Workspace; use Illuminate\Cache\TaggableStore; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; diff --git a/src/Services/WorkspaceManager.php b/src/Services/WorkspaceManager.php index f3b9a48..0542a52 100644 --- a/src/Services/WorkspaceManager.php +++ b/src/Services/WorkspaceManager.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Services; +namespace Core\Core\Tenant\Services; -use Core\Mod\Tenant\Models\User; -use Core\Mod\Tenant\Models\Workspace; +use Core\Core\Tenant\Models\User; +use Core\Core\Tenant\Models\Workspace; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Cache; use Illuminate\Validation\Rule; diff --git a/src/Services/WorkspaceService.php b/src/Services/WorkspaceService.php index 30d08fc..68d2b7b 100644 --- a/src/Services/WorkspaceService.php +++ b/src/Services/WorkspaceService.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Services; +namespace Core\Core\Tenant\Services; -use Core\Mod\Tenant\Models\Workspace; +use Core\Core\Tenant\Models\Workspace; use Illuminate\Support\Facades\Session; /** diff --git a/src/Services/WorkspaceTeamService.php b/src/Services/WorkspaceTeamService.php index 34dcf0e..c9687a3 100644 --- a/src/Services/WorkspaceTeamService.php +++ b/src/Services/WorkspaceTeamService.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\Services; +namespace Core\Core\Tenant\Services; -use Core\Mod\Tenant\Models\User; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Models\WorkspaceMember; -use Core\Mod\Tenant\Models\WorkspaceTeam; +use Core\Core\Tenant\Models\User; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Models\WorkspaceMember; +use Core\Core\Tenant\Models\WorkspaceTeam; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; diff --git a/src/View/Blade/emails/usage-alert.blade.php b/src/View/Blade/emails/usage-alert.blade.php index ef8290e..7cb3fff 100644 --- a/src/View/Blade/emails/usage-alert.blade.php +++ b/src/View/Blade/emails/usage-alert.blade.php @@ -1,7 +1,7 @@ @php $appName = config('core.app.name', __('core::core.brand.name')); - $isLimit = $threshold === \Core\Mod\Tenant\Models\UsageAlertHistory::THRESHOLD_LIMIT; - $isCritical = $threshold === \Core\Mod\Tenant\Models\UsageAlertHistory::THRESHOLD_CRITICAL; + $isLimit = $threshold === \Core\Core\Tenant\Models\UsageAlertHistory::THRESHOLD_LIMIT; + $isCritical = $threshold === \Core\Core\Tenant\Models\UsageAlertHistory::THRESHOLD_CRITICAL; @endphp diff --git a/src/View/Modal/Admin/EntitlementWebhookManager.php b/src/View/Modal/Admin/EntitlementWebhookManager.php index 7e3ea60..94b8888 100644 --- a/src/View/Modal/Admin/EntitlementWebhookManager.php +++ b/src/View/Modal/Admin/EntitlementWebhookManager.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace Core\Mod\Tenant\View\Modal\Admin; +namespace Core\Core\Tenant\View\Modal\Admin; -use Core\Mod\Tenant\Models\EntitlementWebhook; -use Core\Mod\Tenant\Models\EntitlementWebhookDelivery; -use Core\Mod\Tenant\Models\Workspace; -use Core\Mod\Tenant\Services\EntitlementWebhookService; +use Core\Core\Tenant\Models\EntitlementWebhook; +use Core\Core\Tenant\Models\EntitlementWebhookDelivery; +use Core\Core\Tenant\Models\Workspace; +use Core\Core\Tenant\Services\EntitlementWebhookService; use Illuminate\Contracts\View\View; use Livewire\Attributes\Computed; use Livewire\Attributes\Title; diff --git a/src/View/Modal/Admin/WorkspaceDetails.php b/src/View/Modal/Admin/WorkspaceDetails.php index 8f0c0c0..bebbe16 100644 --- a/src/View/Modal/Admin/WorkspaceDetails.php +++ b/src/View/Modal/Admin/WorkspaceDetails.php @@ -1,9 +1,9 @@ workspace->entitlementLogs() ->with('user', 'feature') @@ -147,7 +147,7 @@ class WorkspaceDetails extends Component } // Usage records - if (class_exists(\Core\Mod\Tenant\Models\UsageRecord::class)) { + if (class_exists(\Core\Core\Tenant\Models\UsageRecord::class)) { try { $usage = $this->workspace->usageRecords() ->with('user', 'feature') @@ -325,7 +325,7 @@ class WorkspaceDetails extends Component #[Computed] public function allPackages() { - return \Core\Mod\Tenant\Models\Package::active() + return \Core\Core\Tenant\Models\Package::active() ->ordered() ->get(); } @@ -333,7 +333,7 @@ class WorkspaceDetails extends Component #[Computed] public function allFeatures() { - return \Core\Mod\Tenant\Models\Feature::active() + return \Core\Core\Tenant\Models\Feature::active() ->orderBy('category') ->orderBy('sort_order') ->get(); @@ -403,7 +403,7 @@ class WorkspaceDetails extends Component public function resolvedEntitlements() { try { - return app(\Core\Mod\Tenant\Services\EntitlementService::class) + return app(\Core\Core\Tenant\Services\EntitlementService::class) ->getUsageSummary($this->workspace); } catch (\Exception $e) { return collect(); @@ -431,7 +431,7 @@ class WorkspaceDetails extends Component return; } - $package = \Core\Mod\Tenant\Models\Package::findOrFail($this->selectedPackageId); + $package = \Core\Core\Tenant\Models\Package::findOrFail($this->selectedPackageId); // Check if already assigned $existing = $this->workspace->workspacePackages() @@ -446,7 +446,7 @@ class WorkspaceDetails extends Component return; } - \Core\Mod\Tenant\Models\WorkspacePackage::create([ + \Core\Core\Tenant\Models\WorkspacePackage::create([ 'workspace_id' => $this->workspace->id, 'package_id' => $package->id, 'status' => 'active', @@ -461,7 +461,7 @@ class WorkspaceDetails extends Component public function removePackage(int $workspacePackageId): void { - $wp = \Core\Mod\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id) + $wp = \Core\Core\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id) ->findOrFail($workspacePackageId); $packageName = $wp->package?->name ?? 'Package'; @@ -474,7 +474,7 @@ class WorkspaceDetails extends Component public function suspendPackage(int $workspacePackageId): void { - $wp = \Core\Mod\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id) + $wp = \Core\Core\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id) ->findOrFail($workspacePackageId); $wp->suspend(); @@ -486,7 +486,7 @@ class WorkspaceDetails extends Component public function reactivatePackage(int $workspacePackageId): void { - $wp = \Core\Mod\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id) + $wp = \Core\Core\Tenant\Models\WorkspacePackage::where('workspace_id', $this->workspace->id) ->findOrFail($workspacePackageId); $wp->reactivate(); @@ -523,7 +523,7 @@ class WorkspaceDetails extends Component return; } - $feature = \Core\Mod\Tenant\Models\Feature::where('code', $this->selectedFeatureCode)->first(); + $feature = \Core\Core\Tenant\Models\Feature::where('code', $this->selectedFeatureCode)->first(); if (! $feature) { $this->actionMessage = 'Feature not found.'; @@ -534,24 +534,24 @@ class WorkspaceDetails extends Component // Map type to boost type constant $boostType = match ($this->entitlementType) { - 'enable' => \Core\Mod\Tenant\Models\Boost::BOOST_TYPE_ENABLE, - 'add_limit' => \Core\Mod\Tenant\Models\Boost::BOOST_TYPE_ADD_LIMIT, - 'unlimited' => \Core\Mod\Tenant\Models\Boost::BOOST_TYPE_UNLIMITED, - default => \Core\Mod\Tenant\Models\Boost::BOOST_TYPE_ENABLE, + 'enable' => \Core\Core\Tenant\Models\Boost::BOOST_TYPE_ENABLE, + 'add_limit' => \Core\Core\Tenant\Models\Boost::BOOST_TYPE_ADD_LIMIT, + 'unlimited' => \Core\Core\Tenant\Models\Boost::BOOST_TYPE_UNLIMITED, + default => \Core\Core\Tenant\Models\Boost::BOOST_TYPE_ENABLE, }; $durationType = $this->entitlementDuration === 'permanent' - ? \Core\Mod\Tenant\Models\Boost::DURATION_PERMANENT - : \Core\Mod\Tenant\Models\Boost::DURATION_DURATION; + ? \Core\Core\Tenant\Models\Boost::DURATION_PERMANENT + : \Core\Core\Tenant\Models\Boost::DURATION_DURATION; - \Core\Mod\Tenant\Models\Boost::create([ + \Core\Core\Tenant\Models\Boost::create([ 'workspace_id' => $this->workspace->id, 'feature_code' => $this->selectedFeatureCode, 'boost_type' => $boostType, 'duration_type' => $durationType, 'limit_value' => $this->entitlementType === 'add_limit' ? $this->entitlementLimit : null, 'consumed_quantity' => 0, - 'status' => \Core\Mod\Tenant\Models\Boost::STATUS_ACTIVE, + 'status' => \Core\Core\Tenant\Models\Boost::STATUS_ACTIVE, 'starts_at' => now(), 'expires_at' => $this->entitlementExpiresAt ? \Carbon\Carbon::parse($this->entitlementExpiresAt) : null, 'metadata' => ['granted_by' => auth()->id(), 'granted_at' => now()->toDateTimeString()], @@ -565,7 +565,7 @@ class WorkspaceDetails extends Component public function removeBoost(int $boostId): void { - $boost = \Core\Mod\Tenant\Models\Boost::where('workspace_id', $this->workspace->id) + $boost = \Core\Core\Tenant\Models\Boost::where('workspace_id', $this->workspace->id) ->findOrFail($boostId); $featureCode = $boost->feature_code; diff --git a/src/View/Modal/Admin/WorkspaceManager.php b/src/View/Modal/Admin/WorkspaceManager.php index f01fa04..ffa7abe 100644 --- a/src/View/Modal/Admin/WorkspaceManager.php +++ b/src/View/Modal/Admin/WorkspaceManager.php @@ -1,9 +1,9 @@ create(); diff --git a/tests/Feature/ProfileTest.php b/tests/Feature/ProfileTest.php index 0c75a4b..3e96182 100644 --- a/tests/Feature/ProfileTest.php +++ b/tests/Feature/ProfileTest.php @@ -1,9 +1,9 @@