Repo rebuild
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Platform\app\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Company\Models\Company;
|
||||
use Modules\Platform\app\Models\LicenseKey;
|
||||
|
||||
class LicenseKeyController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display licence keys.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('platform::licenses.index', [
|
||||
'companies' => Company::orderBy('name')->get(),
|
||||
'licenses' => LicenseKey::with('company')->latest()->paginate(20),
|
||||
'plainKey' => session('plain_license_key'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue a new licence key.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'company_id' => ['required', 'exists:companies,id'],
|
||||
'label' => ['required', 'string', 'max:255'],
|
||||
'plan' => ['required', 'in:standard,professional,enterprise'],
|
||||
'max_users' => ['required', 'integer', 'min:1', 'max:10000'],
|
||||
]);
|
||||
|
||||
$company = Company::findOrFail($data['company_id']);
|
||||
$plainKey = LicenseKey::issueFor($company, $data['label'], $data['plan'], (int) $data['max_users']);
|
||||
|
||||
return back()->with('plain_license_key', $plainKey)->with('status', 'Licence key issued. Copy it now.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Platform\app\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Company\Models\Company;
|
||||
use Modules\Platform\app\Models\Payment;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display company payments.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('platform::payments.index', [
|
||||
'companies' => Company::orderBy('name')->get(),
|
||||
'payments' => Payment::with('company')->latest()->paginate(20),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a manual payment or admin override record.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'company_id' => ['required', 'exists:companies,id'],
|
||||
'amount' => ['required', 'integer', 'min:0'],
|
||||
'currency' => ['required', 'string', 'size:3'],
|
||||
'status' => ['required', 'in:pending,paid,failed,refunded,override'],
|
||||
'description' => ['nullable', 'string', 'max:255'],
|
||||
]);
|
||||
|
||||
Payment::create([
|
||||
...$data,
|
||||
'paid_at' => in_array($data['status'], ['paid', 'override'], true) ? now() : null,
|
||||
'metadata' => ['source' => 'manual_admin'],
|
||||
]);
|
||||
|
||||
return back()->with('status', 'Payment record created.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Platform\app\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Modules\Company\Models\Company;
|
||||
use Modules\Platform\app\Models\LicenseKey;
|
||||
use Modules\Platform\app\Models\Payment;
|
||||
use Modules\Platform\app\Models\PlatformPrice;
|
||||
use Modules\Warehouse\app\Models\Warehouse;
|
||||
|
||||
class PlatformDashboardController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
$companies = Company::query()
|
||||
->withCount(['users', 'warehouses', 'licenseKeys'])
|
||||
->latest('id')
|
||||
->limit(8)
|
||||
->get();
|
||||
|
||||
$prices = PlatformPrice::query()
|
||||
->when(Schema::hasColumn('platform_prices', 'is_active'), static function ($query): void {
|
||||
$query->where('is_active', true);
|
||||
})
|
||||
->when(Schema::hasColumn('platform_prices', 'active'), static function ($query): void {
|
||||
$query->where('active', true);
|
||||
})
|
||||
->orderBy('sort_order')
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
return view('platform::dashboard.index', [
|
||||
'companiesCount' => Company::query()->count(),
|
||||
'activeCompaniesCount' => Company::query()->where('status', 'active')->count(),
|
||||
'licensesCount' => LicenseKey::query()->count(),
|
||||
'warehousesCount' => Warehouse::query()->count(),
|
||||
'pricesCount' => PlatformPrice::query()->count(),
|
||||
'paymentsTotal' => Payment::query()->where('status', 'paid')->sum('amount'),
|
||||
'companies' => $companies,
|
||||
'prices' => $prices,
|
||||
'companyUsageChartLabels' => $companies->pluck('name')->values(),
|
||||
'companyUsageChartSeries' => [
|
||||
'Users' => $companies->pluck('users_count')->values(),
|
||||
'Warehouses' => $companies->pluck('warehouses_count')->values(),
|
||||
'Licences' => $companies->pluck('license_keys_count')->values(),
|
||||
],
|
||||
'platformMixChartLabels' => ['Companies', 'Warehouses', 'Licences'],
|
||||
'platformMixChartValues' => [
|
||||
Company::query()->count(),
|
||||
Warehouse::query()->count(),
|
||||
LicenseKey::query()->count(),
|
||||
],
|
||||
'companyStatusChartLabels' => ['Active', 'Inactive'],
|
||||
'companyStatusChartValues' => [
|
||||
Company::query()->where('status', 'active')->count(),
|
||||
Company::query()->where('status', 'inactive')->count(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Platform\app\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
use Modules\Platform\app\Models\PlatformPrice;
|
||||
|
||||
class PlatformPriceController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the customer-facing platform pricing cards.
|
||||
*/
|
||||
public function publicIndex(): View
|
||||
{
|
||||
return view('platform::prices.public', [
|
||||
'prices' => PlatformPrice::query()
|
||||
->where('is_active', true)
|
||||
->ordered()
|
||||
->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display platform prices in a card grid with administration actions.
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
return view('platform::prices.index', [
|
||||
'prices' => PlatformPrice::ordered()->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the create price form.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('platform::prices.create', [
|
||||
'price' => new PlatformPrice([
|
||||
'currency' => 'gbp',
|
||||
'sort_order' => 40,
|
||||
'is_active' => true,
|
||||
'features' => [],
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new platform price.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
PlatformPrice::create($this->validatedPriceData($request));
|
||||
|
||||
return redirect()->route('platform.prices.index')->with('status', 'Price created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the edit price form.
|
||||
*/
|
||||
public function edit(PlatformPrice $price): View
|
||||
{
|
||||
return view('platform::prices.edit', [
|
||||
'price' => $price,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing platform price.
|
||||
*/
|
||||
public function update(Request $request, PlatformPrice $price): RedirectResponse
|
||||
{
|
||||
$price->update($this->validatedPriceData($request, $price));
|
||||
|
||||
return redirect()->route('platform.prices.index')->with('status', 'Price updated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a platform price.
|
||||
*/
|
||||
public function destroy(PlatformPrice $price): RedirectResponse
|
||||
{
|
||||
$price->delete();
|
||||
|
||||
return redirect()->route('platform.prices.index')->with('status', 'Price deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and normalise price request data.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function validatedPriceData(Request $request, ?PlatformPrice $price = null): array
|
||||
{
|
||||
$data = $request->validate([
|
||||
'code' => ['nullable', 'string', 'max:40', 'unique:platform_prices,code,' . ($price?->id ?? 'NULL')],
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'strapline' => ['nullable', 'string', 'max:255'],
|
||||
'description' => ['nullable', 'string', 'max:2000'],
|
||||
'monthly_amount' => ['required', 'numeric', 'min:0', 'max:999999.99'],
|
||||
'annual_amount' => ['nullable', 'numeric', 'min:0', 'max:999999.99'],
|
||||
'currency' => ['required', 'string', 'size:3'],
|
||||
'badge_label' => ['nullable', 'string', 'max:60'],
|
||||
'features' => ['nullable', 'string', 'max:4000'],
|
||||
'sort_order' => ['required', 'integer', 'min:0', 'max:65535'],
|
||||
'is_featured' => ['nullable', 'boolean'],
|
||||
'is_active' => ['nullable', 'boolean'],
|
||||
]);
|
||||
|
||||
$data['code'] = Str::slug($data['code'] ?: $data['name']);
|
||||
$data['monthly_amount'] = (int) round(((float) $data['monthly_amount']) * 100);
|
||||
$data['annual_amount'] = $data['annual_amount'] === null || $data['annual_amount'] === ''
|
||||
? null
|
||||
: (int) round(((float) $data['annual_amount']) * 100);
|
||||
$data['currency'] = strtolower($data['currency']);
|
||||
$data['features'] = collect(preg_split('/\r\n|\r|\n/', (string) ($data['features'] ?? '')))
|
||||
->map(fn (string $feature): string => trim($feature))
|
||||
->filter()
|
||||
->values()
|
||||
->all();
|
||||
$data['is_featured'] = $request->boolean('is_featured');
|
||||
$data['is_active'] = $request->boolean('is_active');
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Platform\app\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Company\Models\Company;
|
||||
use Modules\Platform\app\Models\Payment;
|
||||
|
||||
class StripeWebhookController extends Controller
|
||||
{
|
||||
/**
|
||||
* Receive a Stripe webhook event.
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$payload = $request->all();
|
||||
$eventType = data_get($payload, 'type');
|
||||
|
||||
if ($eventType === 'payment_intent.succeeded') {
|
||||
$intent = data_get($payload, 'data.object', []);
|
||||
$company = Company::where('stripe_customer_id', data_get($intent, 'customer'))->first();
|
||||
|
||||
if ($company) {
|
||||
Payment::updateOrCreate(
|
||||
['stripe_payment_intent_id' => data_get($intent, 'id')],
|
||||
[
|
||||
'company_id' => $company->id,
|
||||
'amount' => (int) data_get($intent, 'amount_received', 0),
|
||||
'currency' => (string) data_get($intent, 'currency', 'gbp'),
|
||||
'status' => 'paid',
|
||||
'description' => 'Stripe payment received',
|
||||
'paid_at' => now(),
|
||||
'metadata' => $intent,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['received' => true]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user