File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Refactor/Admin/CustomerController.php
<?php
namespace App\Http\Controllers\Refactor\Admin;
use App\Http\Controllers\Controller;
use App\Jobs\RefreshCacheableJob;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Model\Customer;
use App\Model\Plan;
use App\Model\Language;
use App\Model\Role;
use App\Model\User;
use App\Model\Contact;
use App\Model\Country;
class CustomerController extends Controller
{
public function index(Request $request)
{
if (Gate::denies('read', new Customer())) {
return $this->notAuthorized();
}
$admin = $request->user()->admin;
$allCustomers = $admin->getAllCustomers();
$stats = [
'total' => (clone $allCustomers)->count(),
'active' => (clone $allCustomers)->where('customers.status', 'active')->count(),
'inactive' => (clone $allCustomers)->where('customers.status', 'inactive')->count(),
'new_this_month' => (clone $allCustomers)->where('customers.created_at', '>=', now()->startOfMonth())->count(),
];
return view('refactor.pages.admin.customers.index', compact('stats'));
}
public function listing(Request $request)
{
if (Gate::denies('read', new Customer())) {
return response('Unauthorized', 403);
}
$admin = $request->user()->admin;
$query = $admin->getAllCustomers();
// Search — join users table (name/email are on users, not customers)
if ($keyword = $request->keyword) {
$query = $query->leftJoin('users', 'users.customer_id', '=', 'customers.id')
->distinct();
foreach (explode(' ', trim($keyword)) as $word) {
$query = $query->where(function ($q) use ($word) {
$q->where('users.first_name', 'like', "%{$word}%")
->orWhere('users.last_name', 'like', "%{$word}%")
->orWhere('users.email', 'like', "%{$word}%")
->orWhere('customers.name', 'like', "%{$word}%");
});
}
}
// Status filter
if ($request->status && $request->status !== 'all') {
$query = $query->where('customers.status', $request->status);
}
// Sort
$sortBy = $request->sort_by ?? 'customers.created_at';
$sortDir = $request->sort_direction ?? 'desc';
$query = $query->orderBy($sortBy, $sortDir);
$customers = $query->paginate($request->per_page ?? 15);
return view('refactor.pages.admin.customers._list', compact('customers'));
}
public function create(Request $request)
{
if (Gate::denies('create', new Customer())) {
return $this->notAuthorized();
}
$customer = Customer::newCustomer();
$roles = Role::active()->get();
return view('refactor.pages.admin.customers.create', compact('customer', 'roles'));
}
public function store(Request $request)
{
if (Gate::denies('create', new Customer())) {
return $this->notAuthorized();
}
list($validator, $customer, $user) = app(\App\Services\AccountManagement\AccountProvisioningService::class)->createCustomer(
$request->user()->admin,
$request->name,
$request->timezone,
$request->language_id,
$request->email,
$request->password,
$request->password_confirmation,
$request->first_name,
$request->last_name,
$request->image,
$request->role_uid
);
if (!$validator->errors()->isEmpty()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
return redirect()->route('refactor.admin.customers.index')
->with('success', trans('refactor/admin_customers.flash.created'));
}
public function edit(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return $this->notAuthorized();
}
return view('refactor.pages.admin.customers.edit', compact('customer'));
}
/**
* Per-row refresh button on /admin/customers — does two different jobs of work
* because two stale-source domains share the row:
*
* 1. CREDITS — `subscription_credit_state.remaining` mirrors the live file
* tracker `storage/app/quota/*`. Production sends decrement only the
* tracker; the DB row is the snapshot the UI reads. We sync it inline
* via `CreditsService::syncSnapshotFromTracker` (small: 1 file read +
* a few row UPDATEs) and return the fresh values so the JS can update
* the row's progress bar without a page reload.
*
* 2. CACHE — LocalCustomer manifest entries (SubscriberCount, EmailMetrics_*,
* etc.) are Laravel-cache blobs. Some of these compute over big tables
* and aren't safe to run inline (docs/cache §2 "no auto warm-on-miss").
* Dispatched as a fan-out via `RefreshCacheableJob::dispatchAll` so each
* entry is bounded independently.
*
* Response shape mirrors the customer-side `AccountController::refreshCredits`:
* 200 + `{credits, last_synced_at}` lets `CreditRefresh.js` update credit
* spans in place. The `cache_queued` flag tells the JS to also surface a
* toast — "subscriber count refreshing, reload later".
*/
public function refreshCache(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('read', $customer)) {
return $this->notAuthorized();
}
// Credits — inline DB-row sync (fast)
$credits = [];
$sub = $customer->getCurrentActiveSubscription();
if ($sub !== null) {
$credits = app(\App\Services\Plans\Credits\CreditsService::class)
->syncSnapshotFromTracker($sub);
}
// Cache — async fan-out (each manifest entry as its own job)
RefreshCacheableJob::dispatchAll($customer->local());
return response()->json([
'credits' => $credits,
'last_synced_at' => now()->toIso8601String(),
'cache_queued' => true,
'message' => trans('refactor/credits.refresh_dispatched'),
]);
}
public function update(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return $this->notAuthorized();
}
$validator = $customer->updateCustomer(
$request->name,
$request->timezone,
$request->language_id
);
if (is_object($validator) && method_exists($validator, 'fails') && $validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Refresh per-customer cache (subscriptions, credits) — listener: UpdateUserJob.
event(new \App\Events\UserUpdated($customer));
return redirect()->route('refactor.admin.customers.index')
->with('success', trans('refactor/admin_customers.flash.updated'));
}
public function select2(Request $request)
{
return response(Customer::select2($request))
->header('Content-Type', 'application/json');
}
public function enable(Request $request)
{
$uids = is_array($request->uids) ? $request->uids : explode(',', $request->uids);
foreach ($uids as $uid) {
$customer = Customer::findByUid($uid);
if ($customer && Gate::allows('update', $customer)) {
$customer->enable();
}
}
return response()->json([
'message' => trans('refactor/admin_customers.flash.enabled'),
]);
}
public function disable(Request $request)
{
$uids = is_array($request->uids) ? $request->uids : explode(',', $request->uids);
foreach ($uids as $uid) {
$customer = Customer::findByUid($uid);
if ($customer && Gate::allows('update', $customer)) {
$customer->disable();
}
}
return response()->json([
'message' => trans('refactor/admin_customers.flash.disabled'),
]);
}
public function delete(Request $request)
{
$uids = is_array($request->uids) ? $request->uids : explode(',', $request->uids);
foreach ($uids as $uid) {
$customer = Customer::findByUid($uid);
if ($customer && Gate::allows('delete', $customer)) {
$customer->deleteAccount();
}
}
return response()->json([
'message' => trans('refactor/admin_customers.flash.deleted'),
]);
}
public function loginAs(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (!$customer) {
return redirect()->back()->with('error', 'Customer not found');
}
// Find first user of this customer
$user = $customer->users()->first();
if (!$user) {
return redirect()->back()->with('error', 'No user found for this customer');
}
// Save admin user ID in session to allow login-back
$request->session()->put('orig_admin_user_id', $request->user()->id);
\Auth::login($user);
return redirect()->route('refactor.dashboard');
}
public function loginBack(Request $request)
{
$adminUserId = $request->session()->get('orig_admin_user_id');
if ($adminUserId) {
$user = \App\Model\User::find($adminUserId);
if ($user && $user->admin) {
\Auth::login($user);
$request->session()->forget('orig_admin_user_id');
return redirect()->route('refactor.admin.dashboard');
}
}
return redirect('/');
}
public function subscriptions(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('read', $customer)) {
return $this->notAuthorized();
}
$subscriptions = $customer->subscriptions()
->orderBy('created_at', 'desc')
->paginate(10);
return view('refactor.pages.admin.customers.subscriptions', compact('customer', 'subscriptions'));
}
public function growthChart(Request $request)
{
$admin = $request->user()->admin;
$data = [];
$columns = [];
for ($i = 5; $i >= 0; $i--) {
$date = now()->subMonths($i);
$columns[] = $date->format('m/Y');
$data[] = Customer::customersCountByTime(
$date->startOfMonth(),
$date->endOfMonth(),
$admin
);
}
return response()->json([
'columns' => $columns,
'data' => $data,
]);
}
public function assignPlan(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('assignPlan', $customer)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
if ($request->isMethod('post')) {
$plan = Plan::findByUid($request->plan_uid);
app(\App\Services\Subscription\SubscriptionManagementService::class)
->assignPlan($customer, $plan);
return response()->json([
'message' => trans('refactor/admin_customers.flash.plan_assigned'),
]);
}
// GET — return plans list for popup
$plans = Plan::where('status', 'active')->get();
$currentSub = $customer->getCurrentActiveSubscription();
return view('refactor.pages.admin.customers._assign_plan', compact('customer', 'plans', 'currentSub'));
}
public function contact(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return $this->notAuthorized();
}
$contact = $customer->contact ?? new Contact();
$countries = Country::getSelectOptions();
return view('refactor.pages.admin.customers.contact', compact('customer', 'contact', 'countries'));
}
public function contactUpdate(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return $this->notAuthorized();
}
$validator = \Validator::make($request->all(), Contact::$rules);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$contact = $customer->contact ?? new Contact();
$contact->fill($request->only([
'first_name', 'last_name', 'company', 'phone', 'email',
'zip', 'state', 'city', 'address_1', 'address_2',
'country_id', 'url', 'tax_number', 'billing_address',
]));
$contact->save();
if (!$customer->contact_id) {
$customer->contact_id = $contact->id;
$customer->save();
}
return redirect()->route('refactor.admin.customers.contact', $customer->uid)
->with('success', trans('refactor/admin_customers.flash.contact_updated'));
}
public function oneClickLogin(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (!$customer) {
return response('Customer not found', 404);
}
// If user_uid is provided, use that specific user; otherwise use first user
if ($request->user_uid) {
$user = User::findByUid($request->user_uid);
} else {
$user = $customer->users()->first();
}
if (!$user) {
return response('User not found', 404);
}
$url = $user->generateOneClickLoginUrl();
return view('refactor.pages.admin.customers._one_click_login', compact('url'));
}
public function users(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('read', $customer)) {
return $this->notAuthorized();
}
return view('refactor.pages.admin.customers.users', compact('customer'));
}
public function usersListing(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('read', $customer)) {
return response('Unauthorized', 403);
}
$query = $customer->users();
// Search
if ($keyword = $request->keyword) {
$query = $query->where(function ($q) use ($keyword) {
$q->where('users.first_name', 'like', "%{$keyword}%")
->orWhere('users.last_name', 'like', "%{$keyword}%")
->orWhere('users.email', 'like', "%{$keyword}%");
});
}
// Status filter
if ($request->status && $request->status !== 'all') {
if ($request->status === 'active') {
$query = $query->where('users.activated', true);
} elseif ($request->status === 'inactive') {
$query = $query->where('users.activated', false);
}
}
$query = $query->orderBy('users.created_at', 'desc');
$users = $query->paginate($request->per_page ?? 15);
return view('refactor.pages.admin.customers._users_list', compact('customer', 'users'));
}
public function userCreate(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return $this->notAuthorized();
}
$user = new User();
$roles = Role::active()->get();
return view('refactor.pages.admin.customers.user_form', compact('customer', 'user', 'roles'));
}
public function userStore(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
$validator = \Validator::make($request->all(), [
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6|confirmed',
'first_name' => 'nullable|string|max:255',
'last_name' => 'nullable|string|max:255',
]);
if ($validator->fails()) {
if ($request->ajax()) {
return response()->json(['errors' => $validator->errors()], 422);
}
return redirect()->back()
->withErrors($validator)
->withInput();
}
$user = new User();
$user->email = $request->email;
$user->first_name = $request->first_name;
$user->last_name = $request->last_name;
$user->password = bcrypt($request->password);
$user->customer_id = $customer->id;
$user->activated = true;
$user->save();
if ($request->role_uid) {
$role = Role::findByUid($request->role_uid);
if ($role) {
$user->setRole($role);
}
}
// Handle image upload
if ($request->hasFile('image')) {
$user->uploadProfileImage($request->file('image'));
}
if ($request->ajax()) {
return response()->json([
'message' => trans('refactor/admin_customers.flash.user_created'),
]);
}
return redirect()->route('refactor.admin.customers.users', $customer->uid)
->with('success', trans('refactor/admin_customers.flash.user_created'));
}
public function userEdit(Request $request, $uid, $user_uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return $this->notAuthorized();
}
$user = User::findByUid($user_uid);
$roles = Role::active()->get();
return view('refactor.pages.admin.customers.user_form', compact('customer', 'user', 'roles'));
}
public function userUpdate(Request $request, $uid, $user_uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
$user = User::findByUid($user_uid);
$rules = [
'email' => 'required|email|unique:users,email,' . $user->id,
'first_name' => 'nullable|string|max:255',
'last_name' => 'nullable|string|max:255',
];
if ($request->password) {
$rules['password'] = 'min:6|confirmed';
}
$validator = \Validator::make($request->all(), $rules);
if ($validator->fails()) {
if ($request->ajax()) {
return response()->json(['errors' => $validator->errors()], 422);
}
return redirect()->back()
->withErrors($validator)
->withInput();
}
$user->email = $request->email;
$user->first_name = $request->first_name;
$user->last_name = $request->last_name;
if ($request->password) {
$user->password = bcrypt($request->password);
}
$user->save();
if ($request->role_uid) {
$role = Role::findByUid($request->role_uid);
if ($role) {
$user->setRole($role);
}
} else {
// Remove current role if any
$currentRole = $user->getRole();
if ($currentRole) {
$user->roles()->detach($currentRole);
}
}
// Handle image upload
if ($request->hasFile('image')) {
$user->uploadProfileImage($request->file('image'));
}
if ($request->ajax()) {
return response()->json([
'message' => trans('refactor/admin_customers.flash.user_updated'),
]);
}
return redirect()->route('refactor.admin.customers.users', $customer->uid)
->with('success', trans('refactor/admin_customers.flash.user_updated'));
}
public function usersEnable(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
$uids = is_array($request->uids) ? $request->uids : explode(',', $request->uids);
foreach ($uids as $userUid) {
$user = User::findByUid($userUid);
if ($user && $user->customer_id == $customer->id) {
$user->enable();
}
}
return response()->json([
'message' => trans('refactor/admin_customers.flash.users_enabled'),
]);
}
public function usersDisable(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
$uids = is_array($request->uids) ? $request->uids : explode(',', $request->uids);
foreach ($uids as $userUid) {
$user = User::findByUid($userUid);
if ($user && $user->customer_id == $customer->id) {
$user->disable();
}
}
return response()->json([
'message' => trans('refactor/admin_customers.flash.users_disabled'),
]);
}
public function usersDelete(Request $request, $uid)
{
$customer = Customer::findByUid($uid);
if (Gate::denies('update', $customer)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
$uids = is_array($request->uids) ? $request->uids : explode(',', $request->uids);
foreach ($uids as $userUid) {
$user = User::findByUid($userUid);
if ($user && $user->customer_id == $customer->id) {
$user->deleteAndCleanup();
}
}
return response()->json([
'message' => trans('refactor/admin_customers.flash.users_deleted'),
]);
}
public function userLoginAs(Request $request, $uid, $user_uid)
{
$customer = Customer::findByUid($uid);
if (!$customer) {
return redirect()->back()->with('error', 'Customer not found');
}
$user = User::findByUid($user_uid);
if (!$user || $user->customer_id != $customer->id) {
return redirect()->back()->with('error', 'User not found');
}
// Save admin user ID in session to allow login-back
$request->session()->put('orig_admin_user_id', $request->user()->id);
\Auth::login($user);
return redirect()->route('refactor.dashboard');
}
}