Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions app/Actions/Licenses/DeleteLicense.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace App\Actions\Licenses;

use App\Models\License;
use App\Services\Anystack\Anystack;

class DeleteLicense
{
public function __construct(
protected Anystack $anystack
) {}

/**
* Handle the deletion of a license.
*/
public function handle(License $license, bool $deleteFromAnystack = true): bool
{
if ($deleteFromAnystack) {
$this->anystack->deleteLicense($license->anystack_product_id, $license->anystack_id);
}

return $license->delete();
}
}
76 changes: 29 additions & 47 deletions app/Filament/Resources/LicenseResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
use App\Models\License;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Infolists;
use Filament\Infolists\Infolist;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
Expand All @@ -25,27 +23,39 @@ public static function form(Form $form): Form
->schema([
Forms\Components\Section::make('License Information')
->schema([
Forms\Components\TextInput::make('id'),
Forms\Components\TextInput::make('id')
->disabled(),
Forms\Components\TextInput::make('anystack_id')
->maxLength(36),
->maxLength(36)
->disabled(),
Forms\Components\Select::make('user_id')
->relationship('user', 'email'),
->relationship('user', 'email')
->searchable(['id', 'email']),
Forms\Components\Select::make('subscription_item_id')
->relationship('subscriptionItem', 'id')
->nullable()
->searchable(),
Forms\Components\TextInput::make('policy_name')
->label('Plan'),
Forms\Components\TextInput::make('key'),
Forms\Components\DateTimePicker::make('expires_at'),
Forms\Components\DateTimePicker::make('created_at'),
->label('Plan')
->disabled(),
Forms\Components\TextInput::make('key')
->disabled(),
Forms\Components\DateTimePicker::make('expires_at')
->disabled(),
Forms\Components\DateTimePicker::make('created_at')
->disabled(),
Forms\Components\Toggle::make('is_suspended')
->label('Suspended'),
->label('Suspended')
->disabled(),
])
->columns(2)
->disabled(),
->columns(2),
]);
}

public static function table(Table $table): Table
{
return $table
->defaultSort('id', 'desc')
->columns([
Tables\Columns\TextColumn::make('id')
->sortable()
Expand Down Expand Up @@ -75,7 +85,8 @@ public static function table(Table $table): Table
->sortable(),
])
->filters([
//
Tables\Filters\TernaryFilter::make('is_suspended')
->label('Suspended'),
])
->actions([
Tables\Actions\ActionGroup::make([
Expand All @@ -92,39 +103,10 @@ public static function table(Table $table): Table
->icon('heroicon-m-ellipsis-vertical'),
])
->defaultPaginationPageOption(25)
->bulkActions([]);
}

public static function infolist(Infolist $infolist): Infolist
{
return $infolist
->schema([
Infolists\Components\Section::make('License Information')
->schema([
Infolists\Components\TextEntry::make('id')
->copyable(),
Infolists\Components\TextEntry::make('anystack_id')
->copyable(),
Infolists\Components\TextEntry::make('user.email')
->label('User')
->copyable(),
Infolists\Components\TextEntry::make('policy_name')
->label('Plan')
->copyable(),
Infolists\Components\TextEntry::make('key')
->copyable(),
Infolists\Components\TextEntry::make('expires_at')
->dateTime()
->copyable(),
Infolists\Components\TextEntry::make('created_at')
->dateTime()
->copyable(),
Infolists\Components\IconEntry::make('is_suspended')
->label('Suspended')
->boolean(),
])
->columns(2),
]);
->bulkActions([])
->recordUrl(
fn ($record) => static::getUrl('edit', ['record' => $record])
);
}

public static function getRelations(): array
Expand All @@ -139,7 +121,7 @@ public static function getPages(): array
{
return [
'index' => Pages\ListLicenses::route('/'),
'view' => Pages\ViewLicense::route('/{record}'),
'edit' => Pages\EditLicense::route('/{record}/edit'),
];
}
}
107 changes: 107 additions & 0 deletions app/Filament/Resources/LicenseResource/Pages/EditLicense.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace App\Filament\Resources\LicenseResource\Pages;

use App\Actions\Licenses\DeleteLicense;
use App\Actions\Licenses\SuspendLicense;
use App\Filament\Resources\LicenseResource;
use App\Jobs\UpsertLicenseFromAnystackLicense;
use App\Models\License;
use App\Services\Anystack\Anystack;
use Filament\Actions;
use Filament\Forms\Components\Checkbox;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;

/**
* @property ?License $record
*/
class EditLicense extends EditRecord
{
protected static string $resource = LicenseResource::class;

protected function getHeaderActions(): array
{
return [
Actions\ActionGroup::make([
Actions\Action::make('upsert_from_anystack')
->label('Sync from Anystack')
->icon('heroicon-o-arrow-path')
->color('gray')
->requiresConfirmation()
->modalHeading('Sync License from Anystack')
->modalDescription('This will retrieve the latest license data from Anystack and update the local record.')
->modalSubmitActionLabel('Sync License')
->visible(fn () => filled($this->record->anystack_id))
->action(function () {
try {
$response = app(Anystack::class)->getLicense($this->record->anystack_product_id, $this->record->anystack_id);
dispatch_sync(new UpsertLicenseFromAnystackLicense($response->json('data')));

Notification::make()
->title('License synced')
->body('The license data has been synced.')
->success()
->send();
} catch (\Exception $e) {
Notification::make()
->title('Error syncing license')
->body('Failed to sync license from Anystack: '.$e->getMessage())
->danger()
->send();
}
}),
Actions\Action::make('suspend')
->label('Suspend')
->icon('heroicon-o-archive-box-x-mark')
->color('danger')
->requiresConfirmation()
->modalHeading('Suspend')
->modalDescription('Are you sure you want to suspend this license?')
->modalSubmitActionLabel('Suspend license')
->visible(fn () => ! $this->record->is_suspended)
->action(function () {
app(SuspendLicense::class)->handle($this->record);

Notification::make()
->title('License suspended successfully')
->success()
->send();
}),
Actions\Action::make('delete')
->label('Delete')
->icon('heroicon-o-trash')
->color('danger')
->requiresConfirmation()
->modalHeading('Delete License')
->modalDescription('Are you sure you want to delete this license?')
->modalSubmitActionLabel('Delete')
->form([
Checkbox::make('delete_from_anystack')
->label('Also delete from Anystack')
->default(true),
])
->action(function (array $data) {
try {
app(DeleteLicense::class)->handle($this->record, $data['delete_from_anystack']);

Notification::make()
->title('License deleted successfully')
->success()
->send();

$this->redirect(LicenseResource::getUrl('index'));
} catch (\Exception $e) {
Notification::make()
->title('Error deleting license')
->body('Failed to delete license: '.$e->getMessage())
->danger()
->send();
}
}),
])
->label('Actions')
->icon('heroicon-m-ellipsis-vertical'),
];
}
}
59 changes: 59 additions & 0 deletions app/Filament/Resources/LicenseResource/Pages/ListLicenses.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,69 @@

namespace App\Filament\Resources\LicenseResource\Pages;

use App\Enums\Subscription;
use App\Filament\Resources\LicenseResource;
use App\Jobs\UpsertLicenseFromAnystackLicense;
use App\Models\License;
use App\Services\Anystack\Anystack;
use Filament\Actions;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords;

class ListLicenses extends ListRecords
{
protected static string $resource = LicenseResource::class;

protected function getHeaderActions(): array
{
return [
Actions\Action::make('import_from_anystack')
->label('Import from Anystack')
->icon('heroicon-o-arrow-down-tray')
->color('primary')
->form([
TextInput::make('anystack_id')
->label('Anystack License UUID')
->placeholder('Enter the Anystack license UUID')
->required()
->uuid()
->helperText('Paste the license UUID from Anystack to import it into the system.'),
Select::make('subscription_item_id')
->label('Subscription Item ID')
->placeholder('Enter the subscription item ID')
->nullable()
->helperText('Provide the subscription item ID if this license relates to a subscription.')
->relationship('subscriptionItem', 'id')
->searchable(),
])
->action(function (array $data) {
try {
$productId = Subscription::Mini->anystackProductId();
$response = app(Anystack::class)->getLicense($productId, $data['anystack_id']);
$licenseData = $response->json('data');

dispatch_sync(new UpsertLicenseFromAnystackLicense($licenseData));

if (filled($data['subscription_item_id'] ?? null)) {
$license = License::where('anystack_id', $data['anystack_id'])->firstOrFail();
$license->update(['subscription_item_id' => $data['subscription_item_id']]);
}

Notification::make()
->title('License Imported')
->body('The license was imported.')
->success()
->send();
} catch (\Exception $e) {
Notification::make()
->title('Error importing license')
->body('Failed to import license from Anystack: '.$e->getMessage())
->danger()
->send();
}
}),
];
}
}
46 changes: 0 additions & 46 deletions app/Filament/Resources/LicenseResource/Pages/ViewLicense.php

This file was deleted.

Loading