- Refactor New Quote page to enterprise data-entry layout (2-column, dense) - Add Payment Terms dropdown (COD, NET 15, NET 30, NET 60) - Fix sidebar menu active states and route names - Fix brand filter badge visibility on brands page - Remove company_name references (use business instead) - Polish Promotions page layout - Fix double-click issue on sidebar menu collapse - Make all searches case-insensitive (like -> ilike for PostgreSQL)
320 lines
12 KiB
PHP
320 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Seller;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Brand;
|
|
use App\Models\Business;
|
|
use App\Models\User;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Validation\Rules\Password;
|
|
|
|
class BrandManagerSettingsController extends Controller
|
|
{
|
|
/**
|
|
* Display the brand managers list.
|
|
*/
|
|
public function index(Business $business, Request $request)
|
|
{
|
|
$this->authorizeOwnerAccess($business);
|
|
|
|
// Get all users with contact_type = 'brand_manager' for this business
|
|
$query = $business->users()
|
|
->wherePivot('contact_type', 'brand_manager')
|
|
->with(['brands' => fn ($q) => $q->where('business_id', $business->id)]);
|
|
|
|
// Search
|
|
if ($request->filled('search')) {
|
|
$search = $request->search;
|
|
$query->where(function ($q) use ($search) {
|
|
$q->where('first_name', 'ilike', "%{$search}%")
|
|
->orWhere('last_name', 'ilike', "%{$search}%")
|
|
->orWhere('email', 'ilike', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
$brandManagers = $query->paginate(15);
|
|
|
|
// Get all brands for this business (for the add/edit modal)
|
|
$brands = $business->brands()->orderBy('name')->get();
|
|
|
|
return view('seller.settings.brand-managers', compact('business', 'brandManagers', 'brands'));
|
|
}
|
|
|
|
/**
|
|
* Store a new brand manager.
|
|
*/
|
|
public function store(Business $business, Request $request)
|
|
{
|
|
$this->authorizeOwnerAccess($business);
|
|
|
|
$validated = $request->validate([
|
|
'first_name' => 'required|string|max:255',
|
|
'last_name' => 'required|string|max:255',
|
|
'email' => 'required|email|max:255',
|
|
'phone' => 'nullable|string|max:20',
|
|
'brands' => 'required|array|min:1',
|
|
'brands.*' => 'exists:brands,id',
|
|
'primary_brand_id' => 'nullable|exists:brands,id',
|
|
], [
|
|
'brands.required' => 'Please select at least one brand.',
|
|
'brands.min' => 'Please select at least one brand.',
|
|
]);
|
|
|
|
// Verify all selected brands belong to this business
|
|
$validBrandIds = $business->brands()->whereIn('id', $validated['brands'])->pluck('id')->toArray();
|
|
if (count($validBrandIds) !== count($validated['brands'])) {
|
|
return back()->withErrors(['brands' => 'Invalid brand selection.']);
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
// Check if user already exists
|
|
$user = User::where('email', $validated['email'])->first();
|
|
|
|
if ($user) {
|
|
// Check if user is already a brand manager for this business
|
|
$existingPivot = $user->businesses()
|
|
->where('business_id', $business->id)
|
|
->wherePivot('contact_type', 'brand_manager')
|
|
->first();
|
|
|
|
if ($existingPivot) {
|
|
return back()->withErrors(['email' => 'This user is already a brand manager for this business.']);
|
|
}
|
|
|
|
// Update user details
|
|
$user->update([
|
|
'first_name' => $validated['first_name'],
|
|
'last_name' => $validated['last_name'],
|
|
'phone' => $validated['phone'] ?? $user->phone,
|
|
]);
|
|
} else {
|
|
// Create new user
|
|
$user = User::create([
|
|
'first_name' => $validated['first_name'],
|
|
'last_name' => $validated['last_name'],
|
|
'email' => $validated['email'],
|
|
'phone' => $validated['phone'],
|
|
'password' => Hash::make(\Str::random(32)), // Random password, user will need to reset
|
|
'user_type' => 'seller',
|
|
'status' => 'active',
|
|
]);
|
|
}
|
|
|
|
// Attach user to business with brand_manager contact type
|
|
if (! $user->businesses()->where('business_id', $business->id)->exists()) {
|
|
$user->businesses()->attach($business->id, [
|
|
'contact_type' => 'brand_manager',
|
|
'is_primary' => true,
|
|
'permissions' => json_encode([
|
|
'view_products',
|
|
'view_sales',
|
|
'view_orders',
|
|
'view_inventory',
|
|
'view_promotions',
|
|
'view_conversations',
|
|
'view_buyers',
|
|
'view_analytics',
|
|
]),
|
|
]);
|
|
} else {
|
|
// Update existing pivot to brand_manager
|
|
$user->businesses()->updateExistingPivot($business->id, [
|
|
'contact_type' => 'brand_manager',
|
|
]);
|
|
}
|
|
|
|
// Attach brands to user
|
|
$primaryBrandId = $validated['primary_brand_id'] ?? $validated['brands'][0];
|
|
foreach ($validated['brands'] as $brandId) {
|
|
if (! $user->brands()->where('brand_id', $brandId)->exists()) {
|
|
$user->brands()->attach($brandId, [
|
|
'role' => 'manager',
|
|
'is_primary' => $brandId == $primaryBrandId,
|
|
'permissions' => json_encode([
|
|
'view_sales',
|
|
'view_orders',
|
|
'view_products',
|
|
'view_inventory',
|
|
'view_promotions',
|
|
'view_conversations',
|
|
'view_buyers',
|
|
'view_analytics',
|
|
]),
|
|
]);
|
|
}
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
// TODO: Send invitation email to user with password reset link
|
|
|
|
return redirect()
|
|
->route('seller.business.settings.brand-managers.index', $business->slug)
|
|
->with('success', "Brand manager {$user->name} has been added successfully.");
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
report($e);
|
|
|
|
return back()->withErrors(['error' => 'Failed to create brand manager. Please try again.']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update a brand manager's details and brand assignments.
|
|
*/
|
|
public function update(Business $business, User $user, Request $request)
|
|
{
|
|
$this->authorizeOwnerAccess($business);
|
|
|
|
// Verify user is a brand manager for this business
|
|
$isBrandManager = $user->businesses()
|
|
->where('business_id', $business->id)
|
|
->wherePivot('contact_type', 'brand_manager')
|
|
->exists();
|
|
|
|
if (! $isBrandManager) {
|
|
abort(404, 'Brand manager not found.');
|
|
}
|
|
|
|
$validated = $request->validate([
|
|
'first_name' => 'required|string|max:255',
|
|
'last_name' => 'required|string|max:255',
|
|
'phone' => 'nullable|string|max:20',
|
|
'brands' => 'required|array|min:1',
|
|
'brands.*' => 'exists:brands,id',
|
|
'primary_brand_id' => 'nullable|exists:brands,id',
|
|
], [
|
|
'brands.required' => 'Please select at least one brand.',
|
|
'brands.min' => 'Please select at least one brand.',
|
|
]);
|
|
|
|
// Verify all selected brands belong to this business
|
|
$validBrandIds = $business->brands()->whereIn('id', $validated['brands'])->pluck('id')->toArray();
|
|
if (count($validBrandIds) !== count($validated['brands'])) {
|
|
return back()->withErrors(['brands' => 'Invalid brand selection.']);
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
// Update user details
|
|
$user->update([
|
|
'first_name' => $validated['first_name'],
|
|
'last_name' => $validated['last_name'],
|
|
'phone' => $validated['phone'],
|
|
]);
|
|
|
|
// Sync brands - remove old, add new
|
|
$currentBrandIds = $user->brands()
|
|
->where('business_id', $business->id)
|
|
->pluck('brands.id')
|
|
->toArray();
|
|
|
|
$newBrandIds = $validated['brands'];
|
|
$primaryBrandId = $validated['primary_brand_id'] ?? $newBrandIds[0];
|
|
|
|
// Remove brands that are no longer selected
|
|
$brandsToRemove = array_diff($currentBrandIds, $newBrandIds);
|
|
if (! empty($brandsToRemove)) {
|
|
$user->brands()->detach($brandsToRemove);
|
|
}
|
|
|
|
// Add new brands or update existing
|
|
foreach ($newBrandIds as $brandId) {
|
|
if ($user->brands()->where('brand_id', $brandId)->exists()) {
|
|
// Update is_primary flag
|
|
$user->brands()->updateExistingPivot($brandId, [
|
|
'is_primary' => $brandId == $primaryBrandId,
|
|
]);
|
|
} else {
|
|
// Add new brand
|
|
$user->brands()->attach($brandId, [
|
|
'role' => 'manager',
|
|
'is_primary' => $brandId == $primaryBrandId,
|
|
'permissions' => json_encode([
|
|
'view_sales',
|
|
'view_orders',
|
|
'view_products',
|
|
'view_inventory',
|
|
'view_promotions',
|
|
'view_conversations',
|
|
'view_buyers',
|
|
'view_analytics',
|
|
]),
|
|
]);
|
|
}
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return redirect()
|
|
->route('seller.business.settings.brand-managers.index', $business->slug)
|
|
->with('success', "Brand manager {$user->name} has been updated successfully.");
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
report($e);
|
|
|
|
return back()->withErrors(['error' => 'Failed to update brand manager. Please try again.']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove a brand manager from the business.
|
|
*/
|
|
public function destroy(Business $business, User $user, Request $request)
|
|
{
|
|
$this->authorizeOwnerAccess($business);
|
|
|
|
// Verify user is a brand manager for this business
|
|
$isBrandManager = $user->businesses()
|
|
->where('business_id', $business->id)
|
|
->wherePivot('contact_type', 'brand_manager')
|
|
->exists();
|
|
|
|
if (! $isBrandManager) {
|
|
abort(404, 'Brand manager not found.');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
// Remove all brand assignments for this business
|
|
$businessBrandIds = $business->brands()->pluck('id')->toArray();
|
|
$user->brands()->detach($businessBrandIds);
|
|
|
|
// Remove from business or update contact_type
|
|
$user->businesses()->detach($business->id);
|
|
|
|
DB::commit();
|
|
|
|
return redirect()
|
|
->route('seller.business.settings.brand-managers.index', $business->slug)
|
|
->with('success', "Brand manager {$user->name} has been removed.");
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
report($e);
|
|
|
|
return back()->withErrors(['error' => 'Failed to remove brand manager. Please try again.']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authorize that current user is business owner or super admin.
|
|
*/
|
|
private function authorizeOwnerAccess(Business $business): void
|
|
{
|
|
$user = auth()->user();
|
|
$isOwner = $business->owner_user_id === $user->id;
|
|
$isSuperAdmin = $user->user_type === 'admin';
|
|
|
|
if (! $isOwner && ! $isSuperAdmin) {
|
|
abort(403, 'Only business owners and administrators can manage brand managers.');
|
|
}
|
|
}
|
|
}
|