Add white-labeled marketing portal for dispensary partners:
- Business branding settings table and model for white-labeling
- Portal middleware (EnsureMarketingPortalAccess) with contact_type check
- Portal route group at /portal/{business}/*
- DashboardController with stats and CannaiQ recommendations
- PromoController for viewing recommended/existing promos
- CampaignController with create/send/schedule/cancel
- ListController for managing contact lists
- Policies for MarketingCampaign and MarketingPromo
- White-labeled portal layout with custom branding CSS
- Marketing Portal link in Filament admin for buyer businesses
- MarketingPortalUserSeeder for development testing
- PORTAL_ACCESS.md documentation
76 lines
2.2 KiB
PHP
76 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Portal;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Branding\BusinessBrandingSetting;
|
|
use App\Models\Business;
|
|
use App\Models\Marketing\MarketingPromo;
|
|
use App\Services\Marketing\PromoRecommendationService;
|
|
use Illuminate\Http\Request;
|
|
|
|
class PromoController extends Controller
|
|
{
|
|
public function __construct(
|
|
protected PromoRecommendationService $promoService
|
|
) {}
|
|
|
|
public function index(Request $request, Business $business)
|
|
{
|
|
$branding = BusinessBrandingSetting::forBusiness($business);
|
|
|
|
// Get recommended promos from CannaiQ
|
|
$recommendedPromos = collect();
|
|
try {
|
|
$storeExternalIds = $business->cannaiqStores()
|
|
->pluck('external_id')
|
|
->toArray();
|
|
|
|
if (! empty($storeExternalIds)) {
|
|
$recommendations = $this->promoService->getRecommendations(
|
|
$business,
|
|
$storeExternalIds[0] ?? null,
|
|
limit: 20
|
|
);
|
|
$recommendedPromos = collect($recommendations['recommendations'] ?? []);
|
|
}
|
|
} catch (\Exception $e) {
|
|
// CannaiQ not available
|
|
}
|
|
|
|
// Get existing promos for this business
|
|
$existingPromos = MarketingPromo::forBusiness($business->id)
|
|
->with('brand')
|
|
->when($request->status, fn ($q, $status) => $q->where('status', $status))
|
|
->latest()
|
|
->paginate(12);
|
|
|
|
$statuses = MarketingPromo::getStatuses();
|
|
|
|
return view('portal.promos.index', compact(
|
|
'business',
|
|
'branding',
|
|
'recommendedPromos',
|
|
'existingPromos',
|
|
'statuses'
|
|
));
|
|
}
|
|
|
|
public function show(Request $request, Business $business, MarketingPromo $promo)
|
|
{
|
|
// Ensure promo belongs to this business
|
|
if ($promo->business_id !== $business->id) {
|
|
abort(404);
|
|
}
|
|
|
|
$branding = BusinessBrandingSetting::forBusiness($business);
|
|
$promo->load('brand');
|
|
|
|
return view('portal.promos.show', compact(
|
|
'business',
|
|
'branding',
|
|
'promo'
|
|
));
|
|
}
|
|
}
|