added crypto pm and activation key system
This commit is contained in:
154
app/Filament/Pages/GenerateActivationKeys.php
Normal file
154
app/Filament/Pages/GenerateActivationKeys.php
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Pages;
|
||||||
|
|
||||||
|
use App\Models\ActivationKey;
|
||||||
|
use App\Models\Plan;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Concerns\InteractsWithForms;
|
||||||
|
use Filament\Forms\Contracts\HasForms;
|
||||||
|
use Filament\Pages\Page;
|
||||||
|
use Filament\Tables\Actions\BulkAction;
|
||||||
|
use Filament\Tables\Columns\BooleanColumn;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Concerns\InteractsWithTable;
|
||||||
|
use Filament\Tables\Contracts\HasTable;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Response;
|
||||||
|
use Str;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
|
||||||
|
class GenerateActivationKeys extends Page implements HasForms, HasTable
|
||||||
|
{
|
||||||
|
use InteractsWithForms, InteractsWithTable;
|
||||||
|
|
||||||
|
protected static ?string $navigationIcon = 'heroicon-o-key';
|
||||||
|
protected static string $view = 'filament.pages.generate-activation-keys';
|
||||||
|
protected static ?string $navigationGroup = 'Admin';
|
||||||
|
protected static ?string $title = 'Activation Keys';
|
||||||
|
|
||||||
|
public $plan_id;
|
||||||
|
public $quantity = 1;
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->form->fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getFormSchema(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Select::make('plan_id')
|
||||||
|
->label('Select Plan')
|
||||||
|
->options(Plan::all()->pluck('name', 'id'))
|
||||||
|
->required(),
|
||||||
|
|
||||||
|
TextInput::make('quantity')
|
||||||
|
->numeric()
|
||||||
|
->minValue(1)
|
||||||
|
->maxValue(100)
|
||||||
|
->default(1)
|
||||||
|
->required(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate()
|
||||||
|
{
|
||||||
|
$data = $this->form->getState();
|
||||||
|
$plan = Plan::findOrFail($data['plan_id']);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $data['quantity']; $i++) {
|
||||||
|
ActivationKey::create([
|
||||||
|
'price_id' => $plan->pricing_id,
|
||||||
|
'activation_key' => strtoupper('Z'.Str::random(16)),
|
||||||
|
'is_activated' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title("{$data['quantity']} activation key(s) generated.")
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
$this->form->fill(); // Reset form
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Table Setup ===
|
||||||
|
protected function getTableQuery(): \Illuminate\Database\Eloquent\Builder
|
||||||
|
{
|
||||||
|
return ActivationKey::query()->latest();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTableColumns(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
TextColumn::make('activation_key')
|
||||||
|
->label('Key')
|
||||||
|
->copyable(),
|
||||||
|
|
||||||
|
BooleanColumn::make('is_activated'),
|
||||||
|
|
||||||
|
TextColumn::make('user.email')
|
||||||
|
->label('Activated By'),
|
||||||
|
|
||||||
|
TextColumn::make('billing_interval')
|
||||||
|
->label('Interval')
|
||||||
|
->getStateUsing(function ($record) {
|
||||||
|
$isMonthly = \App\Models\Plan::where('pricing_id', $record->price_id)->value('monthly_billing');
|
||||||
|
return $isMonthly ? 'Monthly' : 'Yearly';
|
||||||
|
}),
|
||||||
|
|
||||||
|
|
||||||
|
TextColumn::make('created_at')
|
||||||
|
->dateTime(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTableFilters(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
SelectFilter::make('is_activated')
|
||||||
|
->options([
|
||||||
|
true => 'Activated',
|
||||||
|
false => 'Not Activated',
|
||||||
|
]),
|
||||||
|
SelectFilter::make('price_id')
|
||||||
|
->label('Plan')
|
||||||
|
->options(
|
||||||
|
Plan::pluck('name', 'pricing_id')
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTableBulkActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
BulkAction::make('Download Keys')
|
||||||
|
->action(fn (Collection $records) => $this->downloadKeys($records))
|
||||||
|
->deselectRecordsAfterCompletion()
|
||||||
|
->requiresConfirmation(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadKeys(Collection $records)
|
||||||
|
{
|
||||||
|
$text = $records->pluck('activation_key')->implode("\n");
|
||||||
|
|
||||||
|
$filename = 'activation_keys_' . now()->timestamp . '.txt';
|
||||||
|
// Store the file in the 'public' directory or a subdirectory within 'public'
|
||||||
|
$path = public_path("activation/{$filename}");
|
||||||
|
|
||||||
|
// Make sure the 'activation' folder exists, create it if it doesn't
|
||||||
|
if (!file_exists(public_path('activation'))) {
|
||||||
|
mkdir(public_path('activation'), 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the contents to the file
|
||||||
|
file_put_contents($path, $text);
|
||||||
|
|
||||||
|
// Return the response that allows users to download the file directly
|
||||||
|
return response()->download($path)->deleteFileAfterSend(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -45,12 +45,21 @@ class PlanResource extends Resource
|
|||||||
TextInput::make('description'),
|
TextInput::make('description'),
|
||||||
TextInput::make('product_id')->required(),
|
TextInput::make('product_id')->required(),
|
||||||
TextInput::make('pricing_id')->required(),
|
TextInput::make('pricing_id')->required(),
|
||||||
|
TextInput::make('shoppy_product_id')->nullable(),
|
||||||
TextInput::make('price')->numeric()->required(),
|
TextInput::make('price')->numeric()->required(),
|
||||||
TextInput::make('mailbox_limit')->numeric()->required(),
|
TextInput::make('mailbox_limit')->numeric()->required(),
|
||||||
Select::make('monthly_billing')->options([
|
Select::make('monthly_billing')->options([
|
||||||
1 => 'Monthly',
|
1 => 'Monthly',
|
||||||
0 => 'Yearly',
|
0 => 'Yearly',
|
||||||
])->default(1)->required(),
|
])->required(),
|
||||||
|
Select::make('accept_stripe')->options([
|
||||||
|
1 => 'Activate',
|
||||||
|
0 => 'Disable',
|
||||||
|
])->required(),
|
||||||
|
Select::make('accept_shoppy')->options([
|
||||||
|
1 => 'Activate',
|
||||||
|
0 => 'Disable',
|
||||||
|
])->required(),
|
||||||
KeyValue::make('details')
|
KeyValue::make('details')
|
||||||
->label('Plan Details (Optional)')
|
->label('Plan Details (Optional)')
|
||||||
->keyPlaceholder('Name')
|
->keyPlaceholder('Name')
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ class Dashboard extends Component
|
|||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
$userId = $user->id;
|
$userId = $user->id;
|
||||||
if ($user->subscribed()) {
|
if ($user->subscribed()) {
|
||||||
$subscription = $user->subscriptions()->where(['stripe_status' => 'active'])->orderByDesc('updated_at')->first();
|
$subscription = $user->subscriptions()
|
||||||
|
//->where(['stripe_status' => 'active'])
|
||||||
|
->orderByDesc('updated_at')
|
||||||
|
->first();
|
||||||
if ($subscription !== null) {
|
if ($subscription !== null) {
|
||||||
$subscriptionId = $subscription->stripe_id;
|
$subscriptionId = $subscription->stripe_id;
|
||||||
$cacheKey = "stripe_check_executed_user_{$userId}_{$subscriptionId}";
|
$cacheKey = "stripe_check_executed_user_{$userId}_{$subscriptionId}";
|
||||||
@@ -74,7 +77,7 @@ class Dashboard extends Component
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cache::put($cacheKey, true, now()->addHour());
|
Cache::put($cacheKey, true, now()->addMinute());
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
\Log::error($exception->getMessage());
|
\Log::error($exception->getMessage());
|
||||||
}
|
}
|
||||||
@@ -176,7 +179,7 @@ class Dashboard extends Component
|
|||||||
$userPriceID = $result['items'][0]['stripe_price'];
|
$userPriceID = $result['items'][0]['stripe_price'];
|
||||||
$subscriptionEnd = $result['ends_at'];
|
$subscriptionEnd = $result['ends_at'];
|
||||||
|
|
||||||
$planName = null; // Default value if not found
|
$planName = null;
|
||||||
|
|
||||||
foreach (config('app.plans') as $plan) {
|
foreach (config('app.plans') as $plan) {
|
||||||
if ($plan['pricing_id'] === $userPriceID) {
|
if ($plan['pricing_id'] === $userPriceID) {
|
||||||
|
|||||||
@@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Dashboard;
|
namespace App\Livewire\Dashboard;
|
||||||
|
|
||||||
|
use App\Models\ActivationKey;
|
||||||
|
use App\Models\Plan;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Pricing extends Component
|
class Pricing extends Component
|
||||||
{
|
{
|
||||||
public $plans;
|
public $plans;
|
||||||
|
public $activation_key;
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
@@ -18,6 +21,70 @@ class Pricing extends Component
|
|||||||
$this->redirect(route('checkout', $pricing_id));
|
$this->redirect(route('checkout', $pricing_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function activateKey(): void
|
||||||
|
{
|
||||||
|
$this->validate([
|
||||||
|
'activation_key' => 'required|alpha_num|max:30',
|
||||||
|
], [
|
||||||
|
'activation_key.required' => 'You must enter an activation key.',
|
||||||
|
'activation_key.alpha_num' => 'The activation key may only contain letters and numbers (no special characters).',
|
||||||
|
'activation_key.max' => 'The activation key must not exceed 30 characters.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$trimmedKey = trim($this->activation_key);
|
||||||
|
$activation = ActivationKey::where('activation_key', $trimmedKey)
|
||||||
|
->where('is_activated', false)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($activation) {
|
||||||
|
if ($activation->price_id !== null) {
|
||||||
|
$result = $this->addSubscription($activation->price_id);
|
||||||
|
}
|
||||||
|
if ($result === true) {
|
||||||
|
$activation->is_activated = true;
|
||||||
|
$activation->user_id = auth()->id();
|
||||||
|
$activation->save();
|
||||||
|
session()->flash('success', 'Activation key is valid and has been activated. Refresh page to see changes.');
|
||||||
|
$this->reset('activation_key');
|
||||||
|
} else {
|
||||||
|
session()->flash('error', 'Something went wrong. Kindly drop a mail at contact@zemail.me to activate your subscription manually.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session()->flash('error', 'Invalid or already activated key.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addSubscription($price_id): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$plan = Plan::where('pricing_id', $price_id)->firstOrFail();
|
||||||
|
$user = auth()->user();
|
||||||
|
$user->createOrGetStripeCustomer();
|
||||||
|
$user->updateStripeCustomer([
|
||||||
|
'address' => [
|
||||||
|
'postal_code' => '10001',
|
||||||
|
'country' => 'US',
|
||||||
|
],
|
||||||
|
'name' => $user->name,
|
||||||
|
'email' => $user->email,
|
||||||
|
]);
|
||||||
|
$user->creditBalance($plan->price * 100, 'Premium Top-up for plan: ' . $plan->name);
|
||||||
|
$balance = $user->balance();
|
||||||
|
$user->newSubscription('default', $plan->pricing_id)->create();
|
||||||
|
|
||||||
|
if ($plan->monthly_billing == 1) {
|
||||||
|
$ends_at = now()->addMonth();
|
||||||
|
} else {
|
||||||
|
$ends_at = now()->addYear();
|
||||||
|
}
|
||||||
|
$user->subscription('default')->cancelAt($ends_at);
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\Log::error($e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.dashboard.pricing');
|
return view('livewire.dashboard.pricing');
|
||||||
|
|||||||
46
app/Models/ActivationKey.php
Normal file
46
app/Models/ActivationKey.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class ActivationKey extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'user_id',
|
||||||
|
'activation_key',
|
||||||
|
'price_id',
|
||||||
|
'is_activated',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'is_activated' => 'boolean',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship: the user who redeemed the activation key (optional).
|
||||||
|
*/
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope to filter unactivated keys
|
||||||
|
*/
|
||||||
|
public function scopeUnactivated($query)
|
||||||
|
{
|
||||||
|
return $query->where('is_activated', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope to filter activated keys
|
||||||
|
*/
|
||||||
|
public function scopeActivated($query)
|
||||||
|
{
|
||||||
|
return $query->where('is_activated', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,16 +7,20 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
class Plan extends Model
|
class Plan extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'product_id',
|
'product_id',
|
||||||
'pricing_id',
|
'pricing_id',
|
||||||
'price',
|
'shoppy_product_id',
|
||||||
'mailbox_limit',
|
'accept_stripe',
|
||||||
'monthly_billing',
|
'accept_shoppy',
|
||||||
'details'
|
'price',
|
||||||
|
'mailbox_limit',
|
||||||
|
'monthly_billing',
|
||||||
|
'details',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'details' => 'json',
|
'details' => 'json',
|
||||||
'monthly_billing' => 'boolean',
|
'monthly_billing' => 'boolean',
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('activation_keys', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||||
|
$table->string('activation_key')->unique();
|
||||||
|
$table->string('price_id')->collation('utf8_bin');
|
||||||
|
$table->boolean('is_activated')->default(false);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('activation_keys');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('plans', function (Blueprint $table) {
|
||||||
|
$table->string('shoppy_product_id')->nullable()->after('pricing_id');
|
||||||
|
$table->boolean('accept_stripe')->default(false)->after('shoppy_product_id');
|
||||||
|
$table->boolean('accept_shoppy')->default(false)->after('accept_stripe');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('plans', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['shoppy_product_id', 'accept_stripe', 'accept_shoppy']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
28
database/seeders/UpdatePlansTableSeeder.php
Normal file
28
database/seeders/UpdatePlansTableSeeder.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Plan;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class UpdatePlansTableSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
Plan::where('id', 1)->update([
|
||||||
|
'shoppy_product_id' => 'MsYfrRX',
|
||||||
|
'accept_stripe' => 1,
|
||||||
|
'accept_shoppy' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Plan::where('id', 2)->update([
|
||||||
|
'shoppy_product_id' => '1oU5SNT',
|
||||||
|
'accept_stripe' => 1,
|
||||||
|
'accept_shoppy' => 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<x-filament::page>
|
||||||
|
<form wire:submit.prevent="generate" class="space-y-6">
|
||||||
|
{{ $this->form }}
|
||||||
|
|
||||||
|
<x-filament::button type="submit">
|
||||||
|
Generate Activation Keys
|
||||||
|
</x-filament::button>
|
||||||
|
</form>
|
||||||
|
{{ $this->table }}
|
||||||
|
</x-filament::page>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<div class="mx-auto max-w-3xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8 ">
|
<div class="mx-auto max-w-3xl px-4 py-8 sm:px-6 sm:py-12 lg:px-8 ">
|
||||||
|
<script src="https://shoppy.gg/api/embed.js"></script>
|
||||||
<div class="w-full mb-8 items-center flex justify-center">
|
<div class="w-full mb-8 items-center flex justify-center">
|
||||||
<h1 class="text-center text-3xl text-gray-900 dark:text-gray-200">Purchase Subscription</h1>
|
<h1 class="text-center text-3xl text-gray-900 dark:text-gray-200">Purchase Subscription</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,12 +36,70 @@
|
|||||||
@endif
|
@endif
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@if($plan->accept_stripe && $plan->pricing_id !== null)
|
||||||
<flux:button variant="primary" class="w-full mt-6 cursor-pointer" wire:click="choosePlan('{{ $plan->pricing_id }}')">
|
<flux:button variant="primary" class="w-full mt-6 cursor-pointer" wire:click="choosePlan('{{ $plan->pricing_id }}')">
|
||||||
Choose Plan
|
Pay with card
|
||||||
</flux:button>
|
</flux:button>
|
||||||
|
@endif
|
||||||
|
@if($plan->accept_shoppy && $plan->shoppy_product_id !== null)
|
||||||
|
<flux:button variant="filled" class="w-full mt-2 cursor-pointer" data-shoppy-product="{{ $plan->shoppy_product_id }}">
|
||||||
|
Pay with crypto
|
||||||
|
</flux:button>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="w-full mt-8">
|
||||||
|
<flux:separator text="or" />
|
||||||
|
<div class="w-full mt-4 mb-8 items-center flex justify-center">
|
||||||
|
<h1 class="text-center text-3xl text-gray-900 dark:text-gray-200">Have an Activation Key?</h1>
|
||||||
|
</div>
|
||||||
|
<div class="flex rounded-lg overflow-hidden border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-zinc-800">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
wire:model="activation_key"
|
||||||
|
class="w-full px-4 py-2 bg-white dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 placeholder-zinc-500 focus:outline-none"
|
||||||
|
placeholder="Enter your activation key"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
wire:click="activateKey"
|
||||||
|
class="cursor-pointer px-5 text-white transition-colors dark:text-white bg-[#4361EE] dark:bg-[#4361EE] disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||||
|
:disabled="wire:loading"
|
||||||
|
>
|
||||||
|
<!-- Show Loader when loading -->
|
||||||
|
<span wire:loading.remove>Activate</span>
|
||||||
|
<span wire:loading class="flex justify-center items-center px-4">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_7NYg{animation:spinner_0KQs 1.2s cubic-bezier(0.52,.6,.25,.99) infinite}@keyframes spinner_0KQs{0%{r:0;opacity:1}100%{r:11px;opacity:0}}</style><circle class="spinner_7NYg" cx="12" cy="12" r="0" fill="white"/></svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Redeem your activation key, purchased with Pay with Crypto option.
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
@error('activation_key')
|
||||||
|
<div class="mt-4 app-primary">
|
||||||
|
{{ $message }}
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
|
<!-- Success/Error Message -->
|
||||||
|
@if (session()->has('success'))
|
||||||
|
<div class="mt-4" style="color: #00AB55">
|
||||||
|
{{ session('success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (session()->has('error'))
|
||||||
|
<div class="mt-4 text-green-700">
|
||||||
|
{{ session('error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user